├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ └── maven-build.yml ├── .gitignore ├── .idea └── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── Torrent.iml ├── pom.xml └── src └── main ├── java ├── Main.java └── com │ └── stream │ ├── common │ ├── CommonConstants.java │ ├── CommonUtils.java │ └── CredentialsDTO.java │ ├── exceptions │ ├── BadTypeException.java │ ├── ConnectionException.java │ ├── ItemNotFoundException.java │ ├── LinkUnavailableException.java │ └── RealDebridException.java │ ├── fetcher │ ├── CssSelector.java │ ├── FetcherUtils.java │ ├── SourceDTO.java │ └── SourceUtils.java │ ├── realdebrid │ ├── DebridUtils.java │ └── dtos │ │ ├── AddMagnetDTO.java │ │ ├── AllTorrentsInfoDTO.java │ │ ├── AuthenticationDTO.java │ │ ├── ClientDTO.java │ │ ├── TokenDTO.java │ │ ├── TorrentFilesDTO.java │ │ ├── TorrentInfoDTO.java │ │ └── UnrestrictDTO.java │ ├── subtitles │ ├── DownloadTask.java │ └── YtsSubtitleUtils.java │ └── ytstorrent │ ├── YtsTorrentUtils.java │ └── dtos │ ├── YtsMovieDTO.java │ └── YtsTorrentDTO.java └── resources └── data ├── app-properties.json └── ytssubs.json /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Version [e.g. 22] 29 | 30 | **Additional context** 31 | Add any other context about the problem here. 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/maven-build.yml: -------------------------------------------------------------------------------- 1 | name: Java CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v2 11 | - name: Set up JDK 1.8 12 | uses: actions/setup-java@v1 13 | with: 14 | java-version: 1.8 15 | - name: Build with Maven 16 | run: mvn -B package --file pom.xml -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /workspace.xml 3 | .idea/ 4 | /src/main/resources/META-INF 5 | out/ 6 | /Torrent.iml 7 | /authentication.txt 8 | /credentials.txt 9 | /target 10 | /support 11 | /src/main/java/META-INF -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /workspace.xml 3 | .idea/ -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at varunbsalian@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to RealDebrid-Torrent-Stream 2 | We love your input! We want to make contributing to this project as easy and transparent as possible, whether it's: 3 | 4 | - Reporting a bug 5 | - Discussing the current state of the code 6 | - Submitting a fix 7 | - Proposing new features 8 | - Becoming a maintainer 9 | 10 | ## We Develop with Github 11 | We use github to host code, to track issues and feature requests, as well as accept pull requests. 12 | 13 | ## We Use [Github Flow](https://guides.github.com/introduction/flow/index.html), So All Code Changes Happen Through Pull Requests 14 | Pull requests are the best way to propose changes to the codebase (we use [Github Flow](https://guides.github.com/introduction/flow/index.html)). We actively welcome your pull requests: 15 | 16 | 1. Fork the repo and create your branch from `master`. 17 | 2. If you've added code that should be tested, add tests. 18 | 3. If you've changed APIs, update the documentation. 19 | 4. Ensure the test suite passes. 20 | 5. Make sure your code lints. 21 | 6. Issue that pull request! 22 | 23 | ## Any contributions you make will be under the MIT Software License 24 | In short, when you submit code changes, your submissions are understood to be under the same [MIT License](http://choosealicense.com/licenses/mit/) that covers the project. Feel free to contact the maintainers if that's a concern. 25 | 26 | ## Report bugs using Github's [issues](https://github.com/varunsalian/RealDebrid-Torrent-Stream/issues) 27 | We use GitHub issues to track public bugs. Report a bug by [opening a new issue](); it's that easy! 28 | 29 | ## Write bug reports with detail, background, and sample code 30 | [This is an example](http://stackoverflow.com/q/12488905/180626) of a bug report, and I think it's not a bad model. Here's [another example from Craig Hockenberry](http://www.openradar.me/11905408), an app developer whom I greatly respect. 31 | 32 | **Great Bug Reports** tend to have: 33 | 34 | - A quick summary and/or background 35 | - Steps to reproduce 36 | - Be specific! 37 | - Give sample code if you can. [this stackoverflow question](http://stackoverflow.com/q/12488905/180626) includes sample code that *anyone* with a base R setup can run to reproduce what I was seeing 38 | - What you expected would happen 39 | - What actually happens 40 | - Notes (possibly including why you think this might be happening, or stuff you tried that didn't work) 41 | 42 | People *love* thorough bug reports. I'm not even kidding. 43 | 44 | ## License 45 | By contributing, you agree that your contributions will be licensed under its MIT License. 46 | 47 | ## References 48 | This document was adapted from the open-source contribution guidelines for [Facebook's Draft](https://github.com/facebook/draft-js/blob/a9316a723f9e918afde44dea68b5f9f39b7d9b00/CONTRIBUTING.md) 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 varunsalian 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | RealDebrid-Torrent-Stream 2 | 3 | General Overview: 4 | 5 | This is a simple console based application written in Java, to stream movies by searching from the torrents (currently supports YIFY) and play it in VLC media player via real-debrid. 6 | 7 | NOTE: This requires a real-debrid account to work. 8 | 9 | Technical Overview 10 | 11 | 1. Search query is taken from System.in. 12 | 2. Using yify api, deatils of all the movies matching the query is fetched. 13 | 3. An api request is sent to Real-debrid to check if the available(cached) torrent hashes match the hashes of the searched torrents. 14 | 4. All the non cached torrents are removed and the user is shown the available torrents. 15 | 5. Suitable Subtitle is downloaded using yifysubtitles. 16 | 5. Once user selects the required option, the streamble link is fetched and a VLC process is started with the streamable URL. 17 | 18 | Direct Download JAR and EXE 19 | 20 | (Will be added soon) 21 | 22 | Usage Instructions 23 | 24 | (Will be added soon) 25 | 26 | TODO List 27 | 28 | 1. Add subtitle support (maybe using opensubtitle) 29 | 2. Add more torrent sources and make it more generic(not specific to movies) 30 | 3. Refactor the code to be more generic 31 | 4. Handle API token of real-debrid. 32 | 33 | 34 | -------------------------------------------------------------------------------- /Torrent.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 37 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.varun.torrent 8 | Torrent 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | org.apache.maven.plugins 14 | maven-compiler-plugin 15 | 16 | 8 17 | 8 18 | 19 | 20 | 21 | 22 | maven-assembly-plugin 23 | 24 | 25 | package 26 | 27 | single 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | Main 37 | 38 | 39 | 40 | 41 | jar-with-dependencies 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | org.json 52 | json 53 | 20190722 54 | 55 | 56 | com.fasterxml.jackson.core 57 | jackson-core 58 | 2.9.6 59 | 60 | 61 | 62 | com.fasterxml.jackson.core 63 | jackson-annotations 64 | 2.9.6 65 | 66 | 67 | 68 | org.apache.httpcomponents 69 | httpclient 70 | 4.5.12 71 | 72 | 73 | 74 | com.fasterxml.jackson.core 75 | jackson-databind 76 | 2.9.10.5 77 | 78 | 79 | 80 | org.springframework 81 | spring-web 82 | 3.0.2.RELEASE 83 | 84 | 85 | 86 | org.jsoup 87 | jsoup 88 | 1.13.1 89 | 90 | 91 | 92 | commons-io 93 | commons-io 94 | 2.6 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /src/main/java/Main.java: -------------------------------------------------------------------------------- 1 | import com.fasterxml.jackson.core.type.TypeReference; 2 | import com.fasterxml.jackson.databind.ObjectMapper; 3 | import com.stream.common.CommonConstants; 4 | import com.stream.common.CommonUtils; 5 | import com.stream.common.CredentialsDTO; 6 | import com.stream.exceptions.*; 7 | import com.stream.realdebrid.DebridUtils; 8 | import com.stream.realdebrid.dtos.*; 9 | import com.stream.subtitles.YtsSubtitleUtils; 10 | import com.stream.ytstorrent.YtsTorrentUtils; 11 | import com.stream.ytstorrent.dtos.YtsMovieDTO; 12 | import com.stream.ytstorrent.dtos.YtsTorrentDTO; 13 | import java.io.*; 14 | import java.net.HttpURLConnection; 15 | import java.net.URL; 16 | import java.util.LinkedHashMap; 17 | import java.util.List; 18 | import java.util.logging.Logger; 19 | 20 | public class Main { 21 | private static Logger logger = Logger.getLogger(Main.class.getName()); 22 | private static ObjectMapper objectMapper = DebridUtils.getObjectMapper(); 23 | 24 | public static void main(String[] args) { 25 | try { 26 | setupProperties(); 27 | DebridUtils.manageRealDebridAuthentication(); 28 | String searchQuery = CommonUtils.askUserSearchQuery(); 29 | ytsMovieStream(searchQuery); 30 | } catch (ItemNotFoundException | ConnectionException | IOException | LinkUnavailableException | RealDebridException | BadTypeException e) { 31 | logger.warning(e.getMessage()); 32 | } catch (InterruptedException e) { 33 | logger.warning(e.getMessage()); 34 | Thread.currentThread().interrupt(); 35 | } 36 | } 37 | 38 | private static void ytsMovieStream(String searchQuery) throws IOException, ItemNotFoundException, ConnectionException, RealDebridException, BadTypeException, InterruptedException, LinkUnavailableException { 39 | //Get Real Debrid access token 40 | //TODO: check if token has expired, (currently new access token is fetched everytime) 41 | CredentialsDTO credentialsDTO = CredentialsDTO.getInstance(); 42 | TokenDTO tokenDTO = credentialsDTO.getTokenDTO(); 43 | 44 | //Search for a movie and select the quality 45 | YtsMovieDTO selectedMovie = YtsTorrentUtils.searchAndSelectMovie(searchQuery); 46 | YtsTorrentDTO selectedTorrent = YtsTorrentUtils.selectQuality(selectedMovie); 47 | 48 | //Get magnet URI of the torrent, add it to real debrid 49 | LinkedHashMap hashMap = new LinkedHashMap<>(); 50 | String magnet = YtsTorrentUtils.magnetGenerator(selectedTorrent.getHash(), selectedMovie.getSlug()); 51 | hashMap.put(CommonConstants.MAGNET, magnet); 52 | String magnetString = DebridUtils.postAndGetData(CommonConstants.DEBRID_API_URL + CommonConstants.ADD_MAGNET_PATH, tokenDTO.getAccessToken(), hashMap); 53 | AddMagnetDTO magnetDTO = objectMapper.readValue(magnetString, AddMagnetDTO.class); 54 | 55 | //Check if the torrent is added to rd and fetch the details 56 | HttpURLConnection httpURLConnection = CommonUtils.getHttpUrlConnection(new URL(magnetDTO.getUri()), tokenDTO.getAccessToken()); 57 | if (httpURLConnection.getResponseCode() != 200) { 58 | throw new ConnectionException(CommonConstants.UNABLE_TO_CONNECT + httpURLConnection.getResponseCode()); 59 | } 60 | String json = CommonUtils.readJSON(httpURLConnection.getInputStream()); 61 | TorrentInfoDTO torrentInfoDTO = objectMapper.readValue(json, TorrentInfoDTO.class); 62 | 63 | //Select the file ID of the file with highest size and add it to download 64 | int selectedFileId = DebridUtils.selectCorrectFileId(torrentInfoDTO); 65 | LinkedHashMap hashMap1 = new LinkedHashMap<>(); 66 | hashMap1.put(CommonConstants.FILES, String.valueOf(selectedFileId)); 67 | DebridUtils.postAndGetData(CommonConstants.DEBRID_API_URL + CommonConstants.SELECT_FILES_PATH + magnetDTO.getId(), tokenDTO.getAccessToken(), hashMap1); 68 | 69 | //Get the list of all downloaded items, pick the selected one. 70 | httpURLConnection = CommonUtils.getHttpUrlConnection(new URL(CommonConstants.DEBRID_API_URL + CommonConstants.TORRENT_INFO_PATH), tokenDTO.getAccessToken()); 71 | if (httpURLConnection.getResponseCode() != 200) { 72 | throw new ConnectionException(CommonConstants.UNABLE_TO_CONNECT + httpURLConnection.getResponseCode()); 73 | } 74 | json = CommonUtils.readJSON(httpURLConnection.getInputStream()); 75 | List allinfo = objectMapper.readValue(json, new TypeReference>() { 76 | }); 77 | String selectedLink = DebridUtils.getLinkOfSelectedTorrentFromTorrentInfo(torrentInfoDTO, allinfo); 78 | 79 | //Unrestrict the link 80 | LinkedHashMap hashMap11 = new LinkedHashMap<>(); 81 | hashMap11.put(CommonConstants.LINK, selectedLink); 82 | String unrestrictString = DebridUtils.postAndGetData(CommonConstants.DEBRID_API_URL + CommonConstants.UNRESTRICT_LINK_PATH, tokenDTO.getAccessToken(), hashMap11); 83 | UnrestrictDTO unrestrictDTO = objectMapper.readValue(unrestrictString, UnrestrictDTO.class); 84 | 85 | //play the video via VLC 86 | YtsSubtitleUtils.addSubtitleFromImdbId(selectedMovie.getImdbCode()); 87 | String subs = CommonUtils.getSubtitleCmdString(selectedMovie.getImdbCode()); 88 | CommonUtils.startVlcProcess(unrestrictDTO.getDownload(), subs); 89 | } 90 | 91 | private static void setupProperties() { 92 | try { 93 | CommonUtils.createDirectoryIfNotExists(CommonConstants.SUPPORT_DIRECTORY); 94 | File targetFile = new File(CommonConstants.SUPPORT_DIRECTORY + CommonConstants.FORWARD_SLASH + CommonConstants.APP_PROPERTIES + CommonConstants.JSON_EXTENSION); 95 | if (!targetFile.exists()) { 96 | InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(CommonConstants.DATA + CommonConstants.FORWARD_SLASH + CommonConstants.APP_PROPERTIES + CommonConstants.JSON_EXTENSION); 97 | if (inputStream != null) { 98 | byte[] buffer = new byte[inputStream.available()]; 99 | inputStream.read(buffer); 100 | OutputStream outStream = new FileOutputStream(targetFile); 101 | outStream.write(buffer); 102 | } 103 | } 104 | } catch (IOException e) { 105 | logger.warning(e.getMessage()); 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/com/stream/common/CommonConstants.java: -------------------------------------------------------------------------------- 1 | package com.stream.common; 2 | 3 | 4 | public final class CommonConstants { 5 | 6 | //HTTP REQUEST CONSTANTS 7 | public static final String HTTP_GET = "GET"; 8 | public static final String HTTP_POST = "POST"; 9 | public static final String USER_AGENT = "User-Agent"; 10 | public static final String USER_AGENT_DETAILS = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11"; 11 | public static final String BEARER = "Bearer "; 12 | public static final String AUTHORIZATION = "Authorization"; 13 | public static final String CONTENT_TYPE = "Content-Type"; 14 | public static final String HTTP_REFERRER = "http://www.google.com"; 15 | public static final String URL_ENCODED_CONTENT_TYPE = "application/x-www-form-urlencoded"; 16 | public static final String CHARSET = "application/x-www-form-urlencoded"; 17 | public static final String UTF_8 = "utf-8"; 18 | public static final String CONTENT_LENGTH = "Content-Length"; 19 | 20 | //YTS SPECIFIC CONSTANTS 21 | public static final String LIST_MOVIRES_YTS_URL = "https://yts.mx/api/v2/list_movies.json"; 22 | public static final String SEARCH_QUERY_YTS = "?query_term="; 23 | public static final String DATA = "data"; 24 | public static final String MOVIES = "movies"; 25 | 26 | //MAGNET SPECIFIC CONSTANTS 27 | public static final String MAGNET_URL = "magnet:?xt=urn:btih:"; 28 | public static final String MAGNET_DOWNLOAD = "&dn="; 29 | public static final String MAGNET_TRACKER = "&tr=http://track.one:1234/announce&tr=udp://track.two:80"; 30 | 31 | //USER INPUT CONSTANTS 32 | public static final String USER_INPUT_MOVIE = "Type the movie name"; 33 | public static final String USER_INPUT_SELECT_OPTION = "Select the option number"; 34 | public static final String USER_INPUT_GOTO = "Goto "; 35 | public static final String USER_INPUT_ENTER_CODE = " and enter this code "; 36 | 37 | //STRING CONSTANTS 38 | public static final String STRING_SPACE = " "; 39 | public static final String EMPTY_STRING = ""; 40 | public static final String STRING_UNDERSCORE = "_"; 41 | public static final String DOT_AND_SPACE = ". "; 42 | public static final String FORWARD_SLASH = "/"; 43 | public static final String SYMBOL_EQUALS = "="; 44 | public static final String SYMBOL_AND = "&"; 45 | public static final String SYMBOL_HYPHEN = "-"; 46 | 47 | //REAL DEBRID SPECIFIC CONSTANTS 48 | public static final String DEBRID_OAUTH_URL = "https://api.real-debrid.com/oauth/v2"; 49 | public static final String DEBRID_SECRET_ID_PATH = "/device/credentials?client_id=X245A4XAIBGVM&code="; 50 | public static final String DEBRID_TOKEN_PATH = "/token"; 51 | public static final String DEBRID_AUTHENTICATION_PATH = "/device/code?client_id=X245A4XAIBGVM&new_credentials=yes"; 52 | public static final String DEBRID_API_URL = "https://api.real-debrid.com/rest/1.0"; 53 | public static final String DEBRID_TORRENT_INSTANT_AVAILABILITY_PATH = "/torrents/instantAvailability/"; 54 | public static final String GRANT_TYPE_URL = "http://oauth.net/grant_type/device/1.0"; 55 | public static final String MAGNET = "magnet"; 56 | public static final String FILES = "files"; 57 | public static final String LINK = "link"; 58 | 59 | //ERROR CONSTANTS 60 | public static final String URL_INVALID = "INVALID URL"; 61 | public static final String ERROR = "ERROR"; 62 | public static final String ERROR_SESSION_TIMED_OUT = "SESSION TIMED OUT"; 63 | public static final String ERROR_AUTHENTICATION_CONNECTION_FAILED = "Failed to connect to Authentication Server"; 64 | 65 | //CLIENT POST 66 | public static final String CLIENT_ID = "client_id"; 67 | public static final String CLIENT_SECRET = "client_secret"; 68 | public static final String DEVICE_CODE = "code"; 69 | public static final String GRANT_TYPE = "grant_type"; 70 | 71 | // DESERIALIZATION CONSTANTS 72 | public static final String AUTHENTICATION_TXT = "support/authentication.txt"; 73 | public static final String CREDENTIALS_TXT = "support/credentials.txt"; 74 | public static final String ADD_MAGNET_PATH = "/torrents/addMagnet"; 75 | public static final String SELECT_FILES_PATH = "/torrents/selectFiles/"; 76 | public static final String TORRENT_INFO_PATH = "/torrents"; 77 | public static final String UNRESTRICT_LINK_PATH = "/unrestrict/link"; 78 | public static final String SUPPORT_DIRECTORY = "support"; 79 | 80 | //SUBTITLE SPECIFIC CONSTANTS 81 | public static final String SUBS_LANGUAGE = "language"; 82 | public static final String YTS_SUBS_SOURCE = "data/ytssubs.json"; 83 | public static final String SUBS_ENGLISH = "English"; 84 | public static final String SUBS_ROOT_FOLDER = "support/subtitle"; 85 | public static final String SUBS_COMPRESSED_FOLDER = "support/subtitle/compressed/"; 86 | public static final String SUBS_UNCOMPRESSED_FOLDER = "support/subtitle/uncompressed/"; 87 | public static final String SUBS_DOWNLOAD_LINK = "link"; 88 | public static final String JSON_EXTENSION = ".json"; 89 | public static final String ZIP_EXTENSION = ".zip"; 90 | public static final String SRT_EXTENSION = ".srt"; 91 | public static final String SUBS_DOWNLOAD_SELECTOR = "a[href].btn-icon"; 92 | public static final String SELECTOR_HREF = "href"; 93 | 94 | 95 | //SYSTEM SPECIFIC CONSTANTS 96 | public static final String VLC_FILEPATH = "C:\\Program Files\\VideoLAN\\VLC\\vlc.exe"; 97 | public static final String VLC_JSON_KEY = "vlc-path"; 98 | public static final String APP_PROPERTIES = "app-properties"; 99 | 100 | //EXCEPTION MESSAGES 101 | public static final String UNABLE_TO_CONNECT = "Unable to connect "; 102 | 103 | //VLC SPECIFIC CONSTANTS 104 | public static final String VLC_ASPECT_RATIO_KEY = "vlc-aspect-ratio"; 105 | public static final String VLC_ASPECT_RATIO_VALUE = "--aspect-ratio"; 106 | public static final String VLC_FULLSCREEN_KEY = "vlc-fullscreen"; 107 | public static final String VLC_FULLSCREEN_VALUE = "--fullscreen"; 108 | public static final String BOOLEAN_TRUE = "true"; 109 | 110 | private CommonConstants() { 111 | 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/com/stream/common/CommonUtils.java: -------------------------------------------------------------------------------- 1 | package com.stream.common; 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.stream.exceptions.LinkUnavailableException; 6 | import com.stream.realdebrid.DebridUtils; 7 | import org.apache.commons.io.IOUtils; 8 | 9 | import java.io.*; 10 | import java.net.HttpURLConnection; 11 | import java.net.URL; 12 | import java.nio.charset.StandardCharsets; 13 | import java.util.*; 14 | import java.util.logging.Logger; 15 | 16 | public final class CommonUtils { 17 | 18 | private static Logger logger = Logger.getLogger(DebridUtils.class.getName()); 19 | 20 | public static HttpURLConnection getHttpUrlConnection(URL url, String token) throws IOException { 21 | HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 22 | conn.setRequestMethod(CommonConstants.HTTP_GET); 23 | conn.setRequestProperty(CommonConstants.USER_AGENT, CommonConstants.USER_AGENT_DETAILS); 24 | if (token != null) { 25 | String bearer = CommonConstants.BEARER + token; 26 | conn.setRequestProperty(CommonConstants.AUTHORIZATION, bearer); 27 | } 28 | conn.connect(); 29 | return conn; 30 | } 31 | 32 | public static String readJSON(InputStream inputStream) throws IOException { 33 | return IOUtils.toString(inputStream, StandardCharsets.UTF_8); 34 | } 35 | 36 | private CommonUtils(){ 37 | 38 | } 39 | 40 | public static String getSubtitleCmdString(String imdbCode) { 41 | File file = new File(CommonConstants.SUBS_UNCOMPRESSED_FOLDER+imdbCode); 42 | File[] files = file.listFiles(); 43 | String result = null; 44 | if(files!=null && files.length>0) { 45 | result = "--sub-file=" + files[0].getAbsolutePath(); 46 | } 47 | return result; 48 | } 49 | 50 | public static String askUserSearchQuery() { 51 | CommonUtils.print(CommonConstants.USER_INPUT_MOVIE); 52 | String searchQuery = CommonUtils.read(); 53 | searchQuery = searchQuery.replace(CommonConstants.STRING_SPACE, CommonConstants.STRING_UNDERSCORE); 54 | return searchQuery; 55 | } 56 | 57 | private static Object getDataFromPropertiesFile(String key) throws IOException { 58 | Object data = null; 59 | ObjectMapper objectMapper = new ObjectMapper(); 60 | File propertiesFile = new File(CommonConstants.SUPPORT_DIRECTORY + CommonConstants.FORWARD_SLASH + CommonConstants.APP_PROPERTIES + CommonConstants.JSON_EXTENSION); 61 | if(propertiesFile.exists()) { 62 | Map properties = objectMapper.readValue(propertiesFile, new TypeReference>() { 63 | }); 64 | data = properties.getOrDefault(key, null); 65 | } 66 | return data; 67 | } 68 | 69 | private static Map getPropertiesFileAsMap() throws IOException { 70 | ObjectMapper objectMapper = new ObjectMapper(); 71 | File propertiesFile = new File(CommonConstants.SUPPORT_DIRECTORY + CommonConstants.FORWARD_SLASH + CommonConstants.APP_PROPERTIES + CommonConstants.JSON_EXTENSION); 72 | Map properties = null; 73 | if(propertiesFile.exists()) { 74 | properties = objectMapper.readValue(propertiesFile, new TypeReference>() { 75 | }); 76 | } 77 | return properties; 78 | } 79 | 80 | public static void startVlcProcess(String videoLink, String subs) throws IOException, LinkUnavailableException { 81 | Object filePath = getDataFromPropertiesFile(CommonConstants.VLC_JSON_KEY); 82 | filePath = filePath==null? CommonConstants.VLC_FILEPATH: filePath; 83 | if(videoLink==null) { 84 | throw new LinkUnavailableException("Streaming Link For the movie is unavailable"); 85 | } 86 | List arguments = new ArrayList<>(); 87 | arguments.add((String) filePath); 88 | arguments.add(videoLink); 89 | processVlcArgumentsFromJson(arguments); 90 | if(subs!=null) { 91 | arguments.add(subs); 92 | } 93 | ProcessBuilder process = new ProcessBuilder(arguments); 94 | process.start(); 95 | } 96 | 97 | private static void processVlcArgumentsFromJson(List arguments) throws IOException { 98 | Map properties = getPropertiesFileAsMap(); 99 | String temp; 100 | if(properties!=null) { 101 | if (properties.containsKey(CommonConstants.VLC_ASPECT_RATIO_KEY) && (temp = (String) properties.get(CommonConstants.VLC_ASPECT_RATIO_KEY)) != null && !temp.equals(CommonConstants.EMPTY_STRING)) { 102 | arguments.add(CommonConstants.VLC_ASPECT_RATIO_VALUE + CommonConstants.SYMBOL_EQUALS + temp); 103 | } 104 | if(properties.containsKey(CommonConstants.VLC_FULLSCREEN_KEY) && (temp = (String) properties.get(CommonConstants.VLC_FULLSCREEN_KEY)) != null && temp.equalsIgnoreCase(CommonConstants.BOOLEAN_TRUE)) { 105 | arguments.add(CommonConstants.VLC_FULLSCREEN_VALUE); 106 | } 107 | } 108 | } 109 | 110 | public static void serializeAnObject(String fileName, Object object) { 111 | try(ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(fileName))) { 112 | objectOutputStream.writeObject(object); 113 | } catch (IOException e) { 114 | logger.warning(e.getMessage()); 115 | } 116 | } 117 | 118 | public static String convertHashesToRequestUrl(List hashes) { 119 | StringBuilder stringBuilder = new StringBuilder(); 120 | for (int i = 0; i < hashes.size(); i++) { 121 | if (i != hashes.size() - 1) { 122 | stringBuilder.append(hashes.get(i)).append(CommonConstants.FORWARD_SLASH); 123 | } 124 | else { 125 | stringBuilder.append(hashes.get(i)); 126 | } 127 | } 128 | return stringBuilder.toString(); 129 | } 130 | 131 | public static void createDirectoryIfNotExists(String directory) { 132 | File file = new File(directory); 133 | if(!file.exists()) { 134 | file.mkdirs(); 135 | } 136 | } 137 | 138 | public static void print(String data){ 139 | if(System.console()!=null){ 140 | System.console().writer().println(data); 141 | } else { 142 | System.out.println(data); 143 | } 144 | } 145 | 146 | public static String read(){ 147 | String data; 148 | if(System.console()!=null){ 149 | data = System.console().readLine(); 150 | } else { 151 | Scanner scanner = new Scanner(System.in); 152 | data = scanner.nextLine(); 153 | } 154 | return data; 155 | } 156 | 157 | } 158 | -------------------------------------------------------------------------------- /src/main/java/com/stream/common/CredentialsDTO.java: -------------------------------------------------------------------------------- 1 | package com.stream.common; 2 | 3 | import com.stream.realdebrid.dtos.AuthenticationDTO; 4 | import com.stream.realdebrid.dtos.ClientDTO; 5 | import com.stream.realdebrid.dtos.TokenDTO; 6 | 7 | import java.util.Objects; 8 | 9 | public final class CredentialsDTO { 10 | 11 | private AuthenticationDTO authenticationDTO; 12 | private ClientDTO clientDTO; 13 | private TokenDTO tokenDTO; 14 | private static CredentialsDTO credentialsDTO; 15 | 16 | public AuthenticationDTO getAuthenticationDTO() { 17 | return authenticationDTO; 18 | } 19 | 20 | public void setAuthenticationDTO(AuthenticationDTO authenticationDTO) { 21 | this.authenticationDTO = authenticationDTO; 22 | } 23 | 24 | public ClientDTO getClientDTO() { 25 | return clientDTO; 26 | } 27 | 28 | public void setClientDTO(ClientDTO clientDTO) { 29 | this.clientDTO = clientDTO; 30 | } 31 | 32 | public TokenDTO getTokenDTO() { 33 | return tokenDTO; 34 | } 35 | 36 | public void setTokenDTO(TokenDTO tokenDTO) { 37 | this.tokenDTO = tokenDTO; 38 | } 39 | 40 | private CredentialsDTO(){ 41 | 42 | } 43 | 44 | public static CredentialsDTO getInstance(){ 45 | if (credentialsDTO==null) { 46 | credentialsDTO = new CredentialsDTO(); 47 | } 48 | return credentialsDTO; 49 | } 50 | 51 | @Override 52 | public boolean equals(Object object) { 53 | if (this == object) { 54 | return true; 55 | } 56 | if (object == null || getClass() != object.getClass()) { 57 | return false; 58 | } 59 | CredentialsDTO that = (CredentialsDTO) object; 60 | return Objects.equals(authenticationDTO, that.authenticationDTO) && 61 | Objects.equals(clientDTO, that.clientDTO) && 62 | Objects.equals(tokenDTO, that.tokenDTO); 63 | } 64 | 65 | @Override 66 | public int hashCode() { 67 | return Objects.hash(authenticationDTO, clientDTO, tokenDTO); 68 | } 69 | 70 | @Override 71 | public String toString() { 72 | return "CredentialsDTO{" + 73 | "authenticationDTO=" + authenticationDTO + 74 | ", clientDTO=" + clientDTO + 75 | ", tokenDTO=" + tokenDTO + 76 | '}'; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/com/stream/exceptions/BadTypeException.java: -------------------------------------------------------------------------------- 1 | package com.stream.exceptions; 2 | 3 | public class BadTypeException extends Exception { 4 | 5 | public BadTypeException(String message){ 6 | super(message); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/stream/exceptions/ConnectionException.java: -------------------------------------------------------------------------------- 1 | package com.stream.exceptions; 2 | 3 | public class ConnectionException extends Exception { 4 | public ConnectionException(String msg){ 5 | super(msg); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/stream/exceptions/ItemNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.stream.exceptions; 2 | 3 | public class ItemNotFoundException extends Exception { 4 | public ItemNotFoundException(String message){ 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/stream/exceptions/LinkUnavailableException.java: -------------------------------------------------------------------------------- 1 | package com.stream.exceptions; 2 | 3 | public class LinkUnavailableException extends Exception { 4 | public LinkUnavailableException(String message){ 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/stream/exceptions/RealDebridException.java: -------------------------------------------------------------------------------- 1 | package com.stream.exceptions; 2 | 3 | public class RealDebridException extends Throwable { 4 | public RealDebridException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/stream/fetcher/CssSelector.java: -------------------------------------------------------------------------------- 1 | package com.stream.fetcher; 2 | 3 | import com.stream.exceptions.BadTypeException; 4 | import org.jsoup.nodes.Document; 5 | import org.jsoup.nodes.Element; 6 | import org.jsoup.select.Elements; 7 | 8 | public class CssSelector { 9 | private static Object selectData(String value, Object elementData) throws BadTypeException{ 10 | if(elementData instanceof Document){ 11 | return ((Document)elementData).select(value); 12 | } 13 | else if(elementData instanceof Elements) { 14 | return ((Elements) elementData).select(value); 15 | } 16 | else if(elementData instanceof Element){ 17 | return ((Element)elementData).select(value); 18 | } 19 | else { 20 | throw new BadTypeException("Unidentified data"); 21 | } 22 | } 23 | 24 | private static Object attrData(String value, Object elementData) throws BadTypeException{ 25 | if(elementData instanceof Document) { 26 | return ((Document) elementData).attr(value); 27 | } 28 | else if(elementData instanceof Elements) { 29 | return ((Elements) elementData).attr(value); 30 | } 31 | else if(elementData instanceof Element) { 32 | return ((Element) elementData).attr(value); 33 | } 34 | else { 35 | throw new BadTypeException("Unidentified data"); 36 | } 37 | } 38 | 39 | private static Object textData(Object elementData) throws BadTypeException{ 40 | if(elementData instanceof Document) { 41 | return ((Document) elementData).text(); 42 | } 43 | else if(elementData instanceof Elements) { 44 | return ((Elements) elementData).text(); 45 | } 46 | else if(elementData instanceof Element) { 47 | return ((Element) elementData).text(); 48 | } 49 | else { 50 | throw new BadTypeException("Unidentified data"); 51 | } 52 | } 53 | 54 | private static Object firstData(Object elementData) throws BadTypeException{ 55 | if(elementData instanceof Elements) { 56 | return ((Elements) elementData).first(); 57 | } 58 | else { 59 | throw new BadTypeException("Unidentified data"); 60 | } 61 | } 62 | 63 | private static Object absUrlData(String value, Object elementData) throws BadTypeException{ 64 | if(elementData instanceof Document) { 65 | return ((Document) elementData).absUrl(value); 66 | } 67 | else if(elementData instanceof Element) { 68 | return ((Element) elementData).absUrl(value); 69 | } 70 | else { 71 | throw new BadTypeException("Unidentified data "); 72 | } 73 | } 74 | 75 | private static Object indexData(String value, Object elementData) throws BadTypeException{ 76 | int index = Integer.parseInt(value); 77 | if(elementData instanceof Elements) { 78 | return ((Elements) elementData).get(index); 79 | } 80 | else { 81 | throw new BadTypeException("Unidentified data"); 82 | } 83 | } 84 | 85 | public static Object getData(String key, String value, Object elementData) throws BadTypeException{ 86 | String newKey = key.split("--")[0]; 87 | switch (newKey){ 88 | case "select": 89 | return selectData(value, elementData); 90 | case "attr": 91 | return attrData(value, elementData); 92 | case "text": 93 | return textData(elementData); 94 | case "first": 95 | return firstData(elementData); 96 | case "absUrl": 97 | return absUrlData(value, elementData); 98 | case "index": 99 | return indexData(value, elementData); 100 | default: 101 | throw new BadTypeException("Unidentified type"); 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/main/java/com/stream/fetcher/FetcherUtils.java: -------------------------------------------------------------------------------- 1 | package com.stream.fetcher; 2 | 3 | 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.stream.exceptions.BadTypeException; 6 | import org.jsoup.Connection; 7 | import org.jsoup.Jsoup; 8 | import org.jsoup.nodes.Document; 9 | import org.jsoup.nodes.Element; 10 | import org.jsoup.select.Elements; 11 | import java.io.IOException; 12 | import java.io.InputStream; 13 | import java.util.ArrayList; 14 | import java.util.HashMap; 15 | import java.util.List; 16 | import java.util.Map; 17 | 18 | public final class FetcherUtils { 19 | public static Document getScrapDataFromUrl(String url) throws IOException { 20 | Connection.Response response = Jsoup.connect(url) 21 | .ignoreContentType(true) 22 | .userAgent("Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11") 23 | .referrer("http://www.google.com") 24 | .timeout(60_000) 25 | .followRedirects(true) 26 | .execute(); 27 | return response.parse(); 28 | } 29 | 30 | private static Object parser(String path, Class dtoClass) throws IOException { 31 | InputStream file = Thread.currentThread().getContextClassLoader().getResourceAsStream(path); 32 | ObjectMapper objectMapper = new ObjectMapper(); 33 | return objectMapper.readValue(file, dtoClass); 34 | } 35 | 36 | private static String getMapKeyByValue(Map map, String value) { 37 | for (Map.Entry s : map.entrySet()) { 38 | if (s.getValue().equals(value)) { 39 | return s.getKey(); 40 | } 41 | } 42 | return null; 43 | } 44 | 45 | public static List> fetchTableFromDocument(SourceDTO torrentSourceDTO, Document document) throws BadTypeException { 46 | int tableIndex = torrentSourceDTO.getTableIndex() == null ? 0 : torrentSourceDTO.getTableIndex(); 47 | List tableContent = torrentSourceDTO.getTableContent(); 48 | Map additionalContentRelation = torrentSourceDTO.getAdditionalContentRelation(); 49 | Element table = document.select("table").get(tableIndex); 50 | Elements rows = table.select("tr"); 51 | Map> contentSpecialCase = torrentSourceDTO.getContentSpecialCase(); 52 | 53 | List headers = new ArrayList<>(tableContent); 54 | headers.addAll(torrentSourceDTO.getAdditionalContent()); 55 | 56 | List> listMap = new ArrayList<>(); 57 | for (int row = 1; row < rows.size(); row++) { 58 | Elements colVals = rows.get(row).select("th,td"); 59 | int colCount = 0; 60 | Map tuple = new HashMap<>(); 61 | for (Element colVal : colVals) { 62 | String currentHeader = headers.get(colCount); 63 | if (additionalContentRelation.containsValue(currentHeader)) { 64 | String tupleData = contentSpecialCase.containsKey(currentHeader) ? selectStringFromCss(contentSpecialCase.get(currentHeader), colVal) : colVal.text(); 65 | tuple.put(headers.get(colCount), tupleData); 66 | String additionalHeader = getMapKeyByValue(additionalContentRelation, currentHeader); 67 | tuple.put(additionalHeader, selectStringFromCss(torrentSourceDTO.getAdditionalContentReference().get(additionalHeader), colVal)); 68 | } else if (contentSpecialCase.containsKey(currentHeader)) { 69 | String tupleData = selectStringFromCss(contentSpecialCase.get(currentHeader), colVal); 70 | tuple.put(headers.get(colCount), tupleData); 71 | } else { 72 | tuple.put(headers.get(colCount), colVal.text()); 73 | } 74 | colCount++; 75 | if(colCount>=headers.size()) { 76 | break; 77 | } 78 | } 79 | listMap.add(tuple); 80 | } 81 | return listMap; 82 | } 83 | 84 | private static String selectStringFromCss(Map data, Element element) throws BadTypeException { 85 | Object elementData = element; 86 | for (Map.Entry entry : data.entrySet()) { 87 | elementData = CssSelector.getData(entry.getKey(), entry.getValue(), elementData); 88 | } 89 | if (elementData instanceof String) { 90 | return (String) elementData; 91 | } 92 | return null; 93 | } 94 | 95 | public static SourceDTO getNextPageSource(Map torrentSourceDTOS, String page) { 96 | return torrentSourceDTOS.get(page); 97 | } 98 | 99 | public static SourceDTO loadSourceFromJson(String sourceName) throws IOException { 100 | return (SourceDTO)parser(sourceName, SourceDTO.class); 101 | } 102 | 103 | 104 | private FetcherUtils(){ 105 | 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/com/stream/fetcher/SourceDTO.java: -------------------------------------------------------------------------------- 1 | package com.stream.fetcher; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | import java.util.Objects; 6 | 7 | public class SourceDTO { 8 | private String sourceName; 9 | private String baseUrl; 10 | private Boolean table; 11 | private Integer tableIndex; 12 | private List tableContent; 13 | private List additionalContent; 14 | private Map additionalContentRelation; 15 | private Map> additionalContentReference; 16 | private Map> headerSpecialCase; 17 | private Map> contentSpecialCase; 18 | private Map nextPage; 19 | 20 | 21 | public Integer getTableIndex() { 22 | return tableIndex; 23 | } 24 | 25 | @Override 26 | public String toString() { 27 | return "com.torrent.fetcher.TorrentSourceDTO{" + 28 | "sourceName='" + sourceName + '\'' + 29 | ", baseUrl='" + baseUrl + '\'' + 30 | ", table=" + table + 31 | ", tableIndex=" + tableIndex + 32 | ", tableContent=" + tableContent + 33 | ", additionalContent=" + additionalContent + 34 | ", additionalContentRelation=" + additionalContentRelation + 35 | ", additionalContentReference=" + additionalContentReference + 36 | ", headerSpecialCase=" + headerSpecialCase + 37 | ", contentSpecialCase=" + contentSpecialCase + 38 | ", nextPage=" + nextPage + 39 | '}'; 40 | } 41 | 42 | public void setTableIndex(Integer tableIndex) { 43 | this.tableIndex = tableIndex; 44 | } 45 | 46 | public Boolean getTable() { 47 | return table; 48 | } 49 | 50 | public void setTable(Boolean table) { 51 | this.table = table; 52 | } 53 | 54 | public String getSourceName() { 55 | return sourceName; 56 | } 57 | 58 | public void setSourceName(String sourceName) { 59 | this.sourceName = sourceName; 60 | } 61 | 62 | public String getBaseUrl() { 63 | return baseUrl; 64 | } 65 | 66 | public void setBaseUrl(String baseUrl) { 67 | this.baseUrl = baseUrl; 68 | } 69 | 70 | 71 | public List getTableContent() { 72 | return tableContent; 73 | } 74 | 75 | public void setTableContent(List tableContent) { 76 | this.tableContent = tableContent; 77 | } 78 | 79 | public List getAdditionalContent() { 80 | return additionalContent; 81 | } 82 | 83 | public void setAdditionalContent(List additionalContent) { 84 | this.additionalContent = additionalContent; 85 | } 86 | 87 | public Map getAdditionalContentRelation() { 88 | return additionalContentRelation; 89 | } 90 | 91 | public void setAdditionalContentRelation(Map additionalContentRelation) { 92 | this.additionalContentRelation = additionalContentRelation; 93 | } 94 | 95 | public Map> getAdditionalContentReference() { 96 | return additionalContentReference; 97 | } 98 | 99 | public void setAdditionalContentReference(Map> additionalContentReference) { 100 | this.additionalContentReference = additionalContentReference; 101 | } 102 | 103 | public Map> getHeaderSpecialCase() { 104 | return headerSpecialCase; 105 | } 106 | 107 | public void setHeaderSpecialCase(Map> headerSpecialCase) { 108 | this.headerSpecialCase = headerSpecialCase; 109 | } 110 | 111 | public Map> getContentSpecialCase() { 112 | return contentSpecialCase; 113 | } 114 | 115 | public void setContentSpecialCase(Map> contentSpecialCase) { 116 | this.contentSpecialCase = contentSpecialCase; 117 | } 118 | 119 | public Map getNextPage() { 120 | return nextPage; 121 | } 122 | 123 | public void setNextPage(Map nextPage) { 124 | this.nextPage = nextPage; 125 | } 126 | 127 | @Override 128 | public boolean equals(Object object) { 129 | if (this == object) { 130 | return true; 131 | } 132 | if (object == null || getClass() != object.getClass()){ 133 | return false; 134 | } 135 | SourceDTO dummyDTO = (SourceDTO) object; 136 | return table == dummyDTO.table && 137 | Objects.equals(sourceName, dummyDTO.sourceName) && 138 | Objects.equals(baseUrl, dummyDTO.baseUrl) && 139 | Objects.equals(tableContent, dummyDTO.tableContent) && 140 | Objects.equals(additionalContent, dummyDTO.additionalContent) && 141 | Objects.equals(additionalContentRelation, dummyDTO.additionalContentRelation) && 142 | Objects.equals(additionalContentReference, dummyDTO.additionalContentReference) && 143 | Objects.equals(headerSpecialCase, dummyDTO.headerSpecialCase) && 144 | Objects.equals(contentSpecialCase, dummyDTO.contentSpecialCase) && 145 | Objects.equals(nextPage, dummyDTO.nextPage); 146 | } 147 | 148 | @Override 149 | public int hashCode() { 150 | return Objects.hash(sourceName, baseUrl, table, tableContent, additionalContent, additionalContentRelation, additionalContentReference, headerSpecialCase, contentSpecialCase, nextPage); 151 | } 152 | 153 | public SourceDTO(){ 154 | 155 | } 156 | } -------------------------------------------------------------------------------- /src/main/java/com/stream/fetcher/SourceUtils.java: -------------------------------------------------------------------------------- 1 | package com.stream.fetcher; 2 | 3 | import com.stream.exceptions.BadTypeException; 4 | import org.jsoup.nodes.Document; 5 | import java.io.IOException; 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | public class SourceUtils { 10 | 11 | public static List> getDataFromSource(SourceDTO torrentSourceDTO, String searchQuery) throws BadTypeException, IOException { 12 | Document document = FetcherUtils.getScrapDataFromUrl(torrentSourceDTO.getBaseUrl()+searchQuery); 13 | return FetcherUtils.fetchTableFromDocument(torrentSourceDTO, document); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/stream/realdebrid/DebridUtils.java: -------------------------------------------------------------------------------- 1 | package com.stream.realdebrid; 2 | 3 | import com.fasterxml.jackson.databind.DeserializationFeature; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.fasterxml.jackson.databind.PropertyNamingStrategy; 6 | import com.stream.common.CommonUtils; 7 | import com.stream.common.CredentialsDTO; 8 | import com.stream.exceptions.ConnectionException; 9 | import com.stream.exceptions.RealDebridException; 10 | import com.stream.realdebrid.dtos.*; 11 | import com.stream.ytstorrent.dtos.YtsMovieDTO; 12 | import com.stream.common.CommonConstants; 13 | import org.json.JSONObject; 14 | import java.io.*; 15 | import java.net.HttpURLConnection; 16 | import java.net.ProtocolException; 17 | import java.net.URL; 18 | import java.nio.charset.StandardCharsets; 19 | import java.util.*; 20 | import java.util.concurrent.atomic.AtomicInteger; 21 | import java.util.logging.Logger; 22 | 23 | public final class DebridUtils { 24 | 25 | private static Logger logger = Logger.getLogger(DebridUtils.class.getName()); 26 | 27 | private static String repeatedCallToGetSecretId(AuthenticationDTO authenticationDTO) throws IOException, InterruptedException, ConnectionException { 28 | URL url1 = new URL(CommonConstants.DEBRID_OAUTH_URL + CommonConstants.DEBRID_SECRET_ID_PATH+ authenticationDTO.getDeviceCode()); 29 | HttpURLConnection httpURLConnection; 30 | for(int i=0;i<120;i++) { 31 | httpURLConnection = CommonUtils.getHttpUrlConnection(url1, null); 32 | if(httpURLConnection.getResponseCode()==200) { 33 | return CommonUtils.readJSON(httpURLConnection.getInputStream()); 34 | } 35 | else if(httpURLConnection.getResponseCode()==400) { 36 | throw new ConnectionException(CommonConstants.URL_INVALID); 37 | } 38 | else { 39 | CommonUtils.print(httpURLConnection.getResponseCode() + " " + httpURLConnection.getResponseMessage()); 40 | CommonUtils.print(CommonConstants.ERROR); 41 | } 42 | Thread.sleep(5000); 43 | } 44 | return null; 45 | } 46 | 47 | private static void setUrlPropertiesForPost(HttpURLConnection urlConnection, String accessToken) throws ProtocolException { 48 | urlConnection.setRequestMethod(CommonConstants.HTTP_POST); 49 | urlConnection.setDoOutput(true); 50 | urlConnection.setInstanceFollowRedirects(false); 51 | urlConnection.setRequestProperty(CommonConstants.CONTENT_TYPE, CommonConstants.URL_ENCODED_CONTENT_TYPE); 52 | urlConnection.setRequestProperty(CommonConstants.CHARSET, CommonConstants.UTF_8); 53 | if(accessToken!=null) { 54 | String bearer = CommonConstants.BEARER + accessToken; 55 | urlConnection.setRequestProperty(CommonConstants.AUTHORIZATION, bearer); 56 | } 57 | urlConnection.setRequestProperty(CommonConstants.USER_AGENT, CommonConstants.USER_AGENT_DETAILS); 58 | } 59 | 60 | private static String generateUrlEncodedAttributesForPost(Map hashMap){ 61 | final String[] data = {CommonConstants.EMPTY_STRING}; 62 | AtomicInteger integer= new AtomicInteger(); 63 | hashMap.forEach((a, b)-> { 64 | if(integer.get() ==0){ 65 | data[0] += a + CommonConstants.SYMBOL_EQUALS + b; 66 | integer.getAndIncrement(); 67 | } 68 | else { 69 | data[0] += CommonConstants.SYMBOL_AND + a + CommonConstants.SYMBOL_EQUALS + b; 70 | } 71 | }); 72 | return data[0]; 73 | } 74 | 75 | private static String postAndGetAccessToken(String clientId, String clientSecret, String deviceCode, String grantType) throws IOException { 76 | URL url; 77 | HttpURLConnection urlConnection = null; 78 | 79 | try { 80 | url = new URL(CommonConstants.DEBRID_OAUTH_URL + CommonConstants.DEBRID_TOKEN_PATH); 81 | urlConnection = (HttpURLConnection) url.openConnection(); 82 | setUrlPropertiesForPost(urlConnection, null); 83 | LinkedHashMap hashMap= new LinkedHashMap<>(); 84 | hashMap.put(CommonConstants.CLIENT_ID, clientId); 85 | hashMap.put(CommonConstants.CLIENT_SECRET, clientSecret); 86 | hashMap.put(CommonConstants.DEVICE_CODE, deviceCode); 87 | hashMap.put(CommonConstants.GRANT_TYPE, grantType); 88 | String data = generateUrlEncodedAttributesForPost(hashMap); 89 | 90 | byte[] postData = data.getBytes( StandardCharsets.UTF_8 ); 91 | int postDataLength = postData.length; 92 | urlConnection.setRequestProperty(CommonConstants.CONTENT_LENGTH, Integer.toString(postDataLength )); 93 | urlConnection.connect(); 94 | DataOutputStream dataOutputStream = new DataOutputStream(urlConnection.getOutputStream()); 95 | dataOutputStream.write(postData); 96 | dataOutputStream.flush(); 97 | if (urlConnection.getResponseCode()==200) { 98 | return CommonUtils.readJSON(urlConnection.getInputStream()); 99 | } 100 | if (urlConnection.getResponseCode()==401) { 101 | return null; 102 | } 103 | } finally { 104 | if (urlConnection != null) { 105 | urlConnection.disconnect(); 106 | } 107 | } 108 | return null; 109 | } 110 | 111 | public static String postAndGetData(String urlString, String accessTokens, Map hashMap) throws IOException { 112 | URL url; 113 | HttpURLConnection urlConnection = null; 114 | 115 | try { 116 | url = new URL(urlString); 117 | urlConnection = (HttpURLConnection) url.openConnection(); 118 | setUrlPropertiesForPost(urlConnection, accessTokens); 119 | String data = generateUrlEncodedAttributesForPost(hashMap); 120 | 121 | byte[] postData = data.getBytes( StandardCharsets.UTF_8 ); 122 | int postDataLength = postData.length; 123 | urlConnection.setRequestProperty(CommonConstants.CONTENT_LENGTH, Integer.toString(postDataLength )); 124 | urlConnection.connect(); 125 | DataOutputStream dataOutputStream = new DataOutputStream(urlConnection.getOutputStream()); 126 | dataOutputStream.write(postData); 127 | dataOutputStream.flush(); 128 | if (urlConnection.getResponseCode()==200 || urlConnection.getResponseCode()==201) { 129 | return CommonUtils.readJSON(urlConnection.getInputStream()); 130 | } 131 | if (urlConnection.getResponseCode()==401) { 132 | return null; 133 | } 134 | } finally { 135 | if (urlConnection != null) { 136 | urlConnection.disconnect(); 137 | } 138 | } 139 | return null; 140 | } 141 | 142 | 143 | private static AuthenticationDTO getAuthenticationDTO() throws IOException { 144 | ObjectMapper objectMapper = new ObjectMapper(); 145 | objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); 146 | objectMapper.configure( 147 | DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); 148 | 149 | URL url = new URL(CommonConstants.DEBRID_OAUTH_URL + CommonConstants.DEBRID_AUTHENTICATION_PATH); 150 | HttpURLConnection httpURLConnection = CommonUtils.getHttpUrlConnection(url, null); 151 | if(httpURLConnection.getResponseCode()==200) { 152 | String jsonString = CommonUtils.readJSON(httpURLConnection.getInputStream()); 153 | AuthenticationDTO authenticationDTO = objectMapper.readValue(jsonString, AuthenticationDTO.class); 154 | CommonUtils.print(CommonConstants.USER_INPUT_GOTO+ authenticationDTO.getVerificationUrl() + CommonConstants.USER_INPUT_ENTER_CODE + authenticationDTO.getUserCode()); 155 | return authenticationDTO; 156 | } 157 | return null; 158 | } 159 | 160 | public static ObjectMapper getObjectMapper() { 161 | ObjectMapper objectMapper = new ObjectMapper(); 162 | objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); 163 | objectMapper.configure( 164 | DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); 165 | return objectMapper; 166 | } 167 | 168 | private static void dooer() throws IOException, InterruptedException, ConnectionException { 169 | AuthenticationDTO authenticationDTO = getAuthenticationDTO(); 170 | if (authenticationDTO==null) { 171 | throw new ConnectionException(CommonConstants.ERROR_AUTHENTICATION_CONNECTION_FAILED); 172 | } 173 | File dataDirectory = new File(CommonConstants.SUPPORT_DIRECTORY); 174 | if(!dataDirectory.exists()) { 175 | dataDirectory.mkdir(); 176 | } 177 | CommonUtils.serializeAnObject(CommonConstants.AUTHENTICATION_TXT, authenticationDTO); 178 | String jsonString = repeatedCallToGetSecretId(authenticationDTO); 179 | if (jsonString==null) { 180 | throw new ConnectionException(CommonConstants.ERROR_AUTHENTICATION_CONNECTION_FAILED); 181 | } 182 | 183 | ObjectMapper objectMapper = getObjectMapper(); 184 | ClientDTO clientDTO = objectMapper.readValue(jsonString,ClientDTO.class); 185 | CommonUtils.print(clientDTO.toString()); 186 | CommonUtils.serializeAnObject(CommonConstants.CREDENTIALS_TXT, clientDTO); 187 | 188 | jsonString = postAndGetAccessToken(clientDTO.getClientID(), clientDTO.getClientSecret(), authenticationDTO.getDeviceCode(), CommonConstants.GRANT_TYPE_URL); 189 | if(jsonString==null){ 190 | throw new ConnectionException(CommonConstants.ERROR_SESSION_TIMED_OUT); 191 | } 192 | TokenDTO tokenDTO = objectMapper.readValue(jsonString, TokenDTO.class); 193 | CommonUtils.print(tokenDTO.toString()); 194 | } 195 | 196 | private static boolean checkIfFileExists(String fileName){ 197 | File file = new File(fileName); 198 | return file.exists(); 199 | } 200 | 201 | private static void deserializeAuthenticationAndClientDTO(AuthenticationDTO authenticationDTO, ClientDTO credentialsDTO) throws IOException, ClassNotFoundException { 202 | authenticationDTO.setAll((AuthenticationDTO) new ObjectInputStream(new FileInputStream(CommonConstants.AUTHENTICATION_TXT)).readObject()); 203 | credentialsDTO.setAll((ClientDTO) new ObjectInputStream(new FileInputStream(CommonConstants.CREDENTIALS_TXT)).readObject()); 204 | } 205 | 206 | public static int selectCorrectFileId(TorrentInfoDTO torrentInfoDTO) { 207 | List files = torrentInfoDTO.getFiles(); 208 | int id=1; 209 | long highestSize = 0; 210 | 211 | for(TorrentFilesDTO file: files){ 212 | if(file.getBytes()>highestSize){ 213 | id= file.getId(); 214 | highestSize= file.getBytes(); 215 | } 216 | } 217 | return id; 218 | } 219 | 220 | public static String getLinkOfSelectedTorrentFromTorrentInfo(TorrentInfoDTO selectedTorrent, List allinfo) throws RealDebridException { 221 | for(AllTorrentsInfoDTO allTorrentsInfoDTO: allinfo){ 222 | if(selectedTorrent.getHash().equals(allTorrentsInfoDTO.getHash()) && !allTorrentsInfoDTO.getLinks().isEmpty()) { 223 | return allTorrentsInfoDTO.getLinks().get(0); 224 | } 225 | } 226 | throw new RealDebridException("File not available in the server"); 227 | } 228 | 229 | public static void removeNonInstantlyAvailableTorrents(List movieDTOS) throws IOException, ConnectionException { 230 | List hashes = getHashesOfTorrentsFromMovieDTO(movieDTOS); 231 | String hashesString = CommonUtils.convertHashesToRequestUrl(hashes); 232 | String json = getRequestToCheckInstantAvailability(hashesString); 233 | JSONObject jsonObject = new JSONObject(json); 234 | Map instantAvailabilityMap = jsonObject.toMap(); 235 | 236 | movieDTOS.forEach(movieDTO -> movieDTO.getTorrents().removeIf(torrentDTO -> !(instantAvailabilityMap.get(torrentDTO.getHash().toLowerCase()) instanceof Map))); 237 | movieDTOS.removeIf(movieDTO -> movieDTO.getTorrents().isEmpty()); 238 | } 239 | 240 | private static String getRequestToCheckInstantAvailability(String hashesString) throws IOException, ConnectionException { 241 | String accessToken = CredentialsDTO.getInstance().getTokenDTO().getAccessToken(); 242 | if(accessToken==null) { 243 | throw new ConnectionException("Credentials Unavailable"); 244 | } 245 | HttpURLConnection checkedConnection = CommonUtils.getHttpUrlConnection(new URL(CommonConstants.DEBRID_API_URL + CommonConstants.DEBRID_TORRENT_INSTANT_AVAILABILITY_PATH + hashesString), accessToken); 246 | return CommonUtils.readJSON(checkedConnection.getInputStream()); 247 | } 248 | 249 | private static List getHashesOfTorrentsFromMovieDTO(List movieDTOS) { 250 | List hashes = new ArrayList<>(); 251 | movieDTOS.forEach(movieDTO -> movieDTO.getTorrents().forEach(torrentDTO -> hashes.add(torrentDTO.getHash()))); 252 | return hashes; 253 | } 254 | 255 | public static void manageRealDebridAuthentication() { 256 | ObjectMapper objectMapper = DebridUtils.getObjectMapper(); 257 | try { 258 | AuthenticationDTO authenticationDTO = new AuthenticationDTO(); 259 | ClientDTO clientDTO = new ClientDTO(); 260 | if (!(DebridUtils.checkIfFileExists(CommonConstants.AUTHENTICATION_TXT) && DebridUtils.checkIfFileExists(CommonConstants.CREDENTIALS_TXT))) { 261 | DebridUtils.dooer(); 262 | } 263 | DebridUtils.deserializeAuthenticationAndClientDTO(authenticationDTO, clientDTO); 264 | String tokenJson = DebridUtils.postAndGetAccessToken(clientDTO.getClientID(), clientDTO.getClientSecret(), authenticationDTO.getDeviceCode(), CommonConstants.GRANT_TYPE_URL); 265 | if (tokenJson == null) { 266 | DebridUtils.dooer(); 267 | DebridUtils.deserializeAuthenticationAndClientDTO(authenticationDTO, clientDTO); 268 | tokenJson = DebridUtils.postAndGetAccessToken(clientDTO.getClientID(), clientDTO.getClientSecret(), authenticationDTO.getDeviceCode(), CommonConstants.GRANT_TYPE_URL); 269 | } 270 | TokenDTO tokenDTO = objectMapper.readValue(tokenJson, TokenDTO.class); 271 | CredentialsDTO credentialsDTO = CredentialsDTO.getInstance(); 272 | 273 | credentialsDTO.setAuthenticationDTO(authenticationDTO); 274 | credentialsDTO.setClientDTO(clientDTO); 275 | credentialsDTO.setTokenDTO(tokenDTO); 276 | } catch (InterruptedException | IOException | ClassNotFoundException | ConnectionException e) { 277 | logger.warning(e.getMessage()); 278 | } 279 | } 280 | 281 | private DebridUtils(){ 282 | 283 | } 284 | } 285 | -------------------------------------------------------------------------------- /src/main/java/com/stream/realdebrid/dtos/AddMagnetDTO.java: -------------------------------------------------------------------------------- 1 | package com.stream.realdebrid.dtos; 2 | 3 | import java.io.Serializable; 4 | 5 | public class AddMagnetDTO implements Serializable { 6 | 7 | 8 | private String id; 9 | private String uri; 10 | 11 | public String getId() { 12 | return id; 13 | } 14 | 15 | @Override 16 | public String toString() { 17 | return "AddMagnetDTO{" + 18 | "id='" + id + '\'' + 19 | ", url='" + uri + '\'' + 20 | '}'; 21 | } 22 | 23 | public void setId(String id) { 24 | this.id = id; 25 | } 26 | 27 | public String getUri() { 28 | return uri; 29 | } 30 | 31 | public void setUri(String uri) { 32 | this.uri = uri; 33 | } 34 | 35 | public AddMagnetDTO(){ 36 | 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/stream/realdebrid/dtos/AllTorrentsInfoDTO.java: -------------------------------------------------------------------------------- 1 | package com.stream.realdebrid.dtos; 2 | 3 | import java.util.List; 4 | 5 | public class AllTorrentsInfoDTO { 6 | 7 | private String id; 8 | private String filename; 9 | private String hash; 10 | private long bytes; 11 | private String host; 12 | private int split; 13 | private int progress; 14 | private String status; 15 | private String added; 16 | private List links; 17 | private String ended; 18 | 19 | @Override 20 | public String toString() { 21 | return "AllTorrentsInfoDTO{" + 22 | "id='" + id + '\'' + 23 | ", filename='" + filename + '\'' + 24 | ", hash='" + hash + '\'' + 25 | ", bytes=" + bytes + 26 | ", host='" + host + '\'' + 27 | ", split=" + split + 28 | ", progress=" + progress + 29 | ", status='" + status + '\'' + 30 | ", added='" + added + '\'' + 31 | ", links=" + links + 32 | ", ended='" + ended + '\'' + 33 | '}'; 34 | } 35 | 36 | public String getId() { 37 | return id; 38 | } 39 | 40 | public void setId(String id) { 41 | this.id = id; 42 | } 43 | 44 | public String getFilename() { 45 | return filename; 46 | } 47 | 48 | public void setFilename(String filename) { 49 | this.filename = filename; 50 | } 51 | 52 | public String getHash() { 53 | return hash; 54 | } 55 | 56 | public void setHash(String hash) { 57 | this.hash = hash; 58 | } 59 | 60 | public long getBytes() { 61 | return bytes; 62 | } 63 | 64 | public void setBytes(long bytes) { 65 | this.bytes = bytes; 66 | } 67 | 68 | public String getHost() { 69 | return host; 70 | } 71 | 72 | public void setHost(String host) { 73 | this.host = host; 74 | } 75 | 76 | public int getSplit() { 77 | return split; 78 | } 79 | 80 | public void setSplit(int split) { 81 | this.split = split; 82 | } 83 | 84 | public int getProgress() { 85 | return progress; 86 | } 87 | 88 | public void setProgress(int progress) { 89 | this.progress = progress; 90 | } 91 | 92 | public String getStatus() { 93 | return status; 94 | } 95 | 96 | public void setStatus(String status) { 97 | this.status = status; 98 | } 99 | 100 | public String getAdded() { 101 | return added; 102 | } 103 | 104 | public void setAdded(String added) { 105 | this.added = added; 106 | } 107 | 108 | public List getLinks() { 109 | return links; 110 | } 111 | 112 | public void setLinks(List links) { 113 | this.links = links; 114 | } 115 | 116 | public String getEnded() { 117 | return ended; 118 | } 119 | 120 | public void setEnded(String ended) { 121 | this.ended = ended; 122 | } 123 | 124 | public AllTorrentsInfoDTO(){ 125 | 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/main/java/com/stream/realdebrid/dtos/AuthenticationDTO.java: -------------------------------------------------------------------------------- 1 | package com.stream.realdebrid.dtos; 2 | 3 | import java.io.Serializable; 4 | 5 | public class AuthenticationDTO implements Serializable { 6 | 7 | private String deviceCode; 8 | private String userCode; 9 | private Integer interval; 10 | private Integer expiresIn; 11 | private String verificationUrl; 12 | 13 | public String getDeviceCode() { 14 | return deviceCode; 15 | } 16 | 17 | public void setDeviceCode(String deviceCode) { 18 | this.deviceCode = deviceCode; 19 | } 20 | 21 | @Override 22 | public String toString() { 23 | return "com.varun.realdebrid.AuthenticationDTO{" + 24 | "deviceCode='" + deviceCode + '\'' + 25 | ", userCode='" + userCode + '\'' + 26 | ", interval=" + interval + 27 | ", expiresIn=" + expiresIn + 28 | ", verificationUrl='" + verificationUrl + '\'' + 29 | '}'; 30 | } 31 | 32 | public String getUserCode() { 33 | return userCode; 34 | } 35 | 36 | public void setUserCode(String userCode) { 37 | this.userCode = userCode; 38 | } 39 | 40 | public Integer getInterval() { 41 | return interval; 42 | } 43 | 44 | public void setInterval(Integer interval) { 45 | this.interval = interval; 46 | } 47 | 48 | public Integer getExpiresIn() { 49 | return expiresIn; 50 | } 51 | 52 | public void setExpiresIn(Integer expiresIn) { 53 | this.expiresIn = expiresIn; 54 | } 55 | 56 | public String getVerificationUrl() { 57 | return verificationUrl; 58 | } 59 | 60 | public void setVerificationUrl(String verificationUrl) { 61 | this.verificationUrl = verificationUrl; 62 | } 63 | 64 | public void setAll(AuthenticationDTO authenticationDTO){ 65 | this.deviceCode = authenticationDTO.deviceCode; 66 | this.userCode = authenticationDTO.userCode; 67 | this.interval = authenticationDTO.interval; 68 | this.expiresIn = authenticationDTO.expiresIn; 69 | this.verificationUrl = authenticationDTO.verificationUrl; 70 | } 71 | 72 | public AuthenticationDTO(){ 73 | 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/com/stream/realdebrid/dtos/ClientDTO.java: -------------------------------------------------------------------------------- 1 | package com.stream.realdebrid.dtos; 2 | 3 | import java.io.Serializable; 4 | 5 | public class ClientDTO implements Serializable { 6 | 7 | private String clientID; 8 | private String clientSecret; 9 | 10 | public String getClientID() { 11 | return clientID; 12 | } 13 | 14 | public void setClientID(String clientID) { 15 | this.clientID = clientID; 16 | } 17 | 18 | public void setAll(ClientDTO clientDTO){ 19 | this.clientID = clientDTO.getClientID(); 20 | this.clientSecret = clientDTO.getClientSecret(); 21 | } 22 | 23 | @Override 24 | public String toString() { 25 | return "AccessTokenDTO{" + 26 | "clientID='" + clientID + '\'' + 27 | ", clientSecret='" + clientSecret + '\'' + 28 | '}'; 29 | } 30 | 31 | public String getClientSecret() { 32 | return clientSecret; 33 | } 34 | 35 | public void setClientSecret(String clientSecret) { 36 | this.clientSecret = clientSecret; 37 | } 38 | 39 | public ClientDTO(){ 40 | 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/stream/realdebrid/dtos/TokenDTO.java: -------------------------------------------------------------------------------- 1 | package com.stream.realdebrid.dtos; 2 | 3 | public class TokenDTO { 4 | private String accessToken; 5 | private String expiresIn; 6 | private String tokenType; 7 | private String refreshToken; 8 | 9 | public String getAccessToken() { 10 | return accessToken; 11 | } 12 | 13 | public void setAccessToken(String accessToken) { 14 | this.accessToken = accessToken; 15 | } 16 | 17 | public String getExpiresIn() { 18 | return expiresIn; 19 | } 20 | 21 | public void setExpiresIn(String expiresIn) { 22 | this.expiresIn = expiresIn; 23 | } 24 | 25 | public String getTokenType() { 26 | return tokenType; 27 | } 28 | 29 | public void setTokenType(String tokenType) { 30 | this.tokenType = tokenType; 31 | } 32 | 33 | public String getRefreshToken() { 34 | return refreshToken; 35 | } 36 | 37 | public void setRefreshToken(String refreshToken) { 38 | this.refreshToken = refreshToken; 39 | } 40 | 41 | @Override 42 | public String toString() { 43 | return "TokenDTO{" + 44 | "accessToken='" + accessToken + '\'' + 45 | ", expiresIn='" + expiresIn + '\'' + 46 | ", tokenType='" + tokenType + '\'' + 47 | ", refreshToken='" + refreshToken + '\'' + 48 | '}'; 49 | } 50 | 51 | public TokenDTO(){ 52 | 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/stream/realdebrid/dtos/TorrentFilesDTO.java: -------------------------------------------------------------------------------- 1 | package com.stream.realdebrid.dtos; 2 | 3 | public class TorrentFilesDTO { 4 | 5 | private int id; 6 | private String path; 7 | private long bytes; 8 | private int selected; 9 | 10 | public int getId() { 11 | return id; 12 | } 13 | 14 | public void setId(int id) { 15 | this.id = id; 16 | } 17 | 18 | public String getPath() { 19 | return path; 20 | } 21 | 22 | public void setPath(String path) { 23 | this.path = path; 24 | } 25 | 26 | public long getBytes() { 27 | return bytes; 28 | } 29 | 30 | public void setBytes(long bytes) { 31 | this.bytes = bytes; 32 | } 33 | 34 | public int getSelected() { 35 | return selected; 36 | } 37 | 38 | public void setSelected(int selected) { 39 | this.selected = selected; 40 | } 41 | 42 | @Override 43 | public String toString() { 44 | return "TorrentFilesDTO{" + 45 | "id=" + id + 46 | ", path='" + path + '\'' + 47 | ", bytes=" + bytes + 48 | ", selected=" + selected + 49 | '}'; 50 | } 51 | 52 | public TorrentFilesDTO(){ 53 | 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/stream/realdebrid/dtos/TorrentInfoDTO.java: -------------------------------------------------------------------------------- 1 | package com.stream.realdebrid.dtos; 2 | 3 | import java.util.List; 4 | 5 | public class TorrentInfoDTO { 6 | private String id; 7 | private String filename; 8 | private String originalFilename; 9 | private String hash; 10 | private long bytes; 11 | private long originalBytes; 12 | private String host; 13 | private int split; 14 | private int progress; 15 | private String status; 16 | private String added; 17 | private List files; 18 | private List links; 19 | private String ended; 20 | private int speed; 21 | private int seeders; 22 | 23 | @Override 24 | public String toString() { 25 | return "TorrentInfoDTO{" + 26 | "id='" + id + '\'' + 27 | ", filename='" + filename + '\'' + 28 | ", original_filename='" + originalFilename + '\'' + 29 | ", hash='" + hash + '\'' + 30 | ", bytes=" + bytes + 31 | ", original_bytes=" + originalBytes + 32 | ", host='" + host + '\'' + 33 | ", split=" + split + 34 | ", progress=" + progress + 35 | ", status='" + status + '\'' + 36 | ", added='" + added + '\'' + 37 | ", files=" + files + 38 | ", links=" + links + 39 | ", ended='" + ended + '\'' + 40 | ", speed=" + speed + 41 | ", seeders=" + seeders + 42 | '}'; 43 | } 44 | 45 | public String getId() { 46 | return id; 47 | } 48 | 49 | public void setId(String id) { 50 | this.id = id; 51 | } 52 | 53 | public String getFilename() { 54 | return filename; 55 | } 56 | 57 | public void setFilename(String filename) { 58 | this.filename = filename; 59 | } 60 | 61 | public String getOriginalFilename() { 62 | return originalFilename; 63 | } 64 | 65 | public void setOriginalFilename(String originalFilename) { 66 | this.originalFilename = originalFilename; 67 | } 68 | 69 | public String getHash() { 70 | return hash; 71 | } 72 | 73 | public void setHash(String hash) { 74 | this.hash = hash; 75 | } 76 | 77 | public long getBytes() { 78 | return bytes; 79 | } 80 | 81 | public void setBytes(long bytes) { 82 | this.bytes = bytes; 83 | } 84 | 85 | public long getOriginalBytes() { 86 | return originalBytes; 87 | } 88 | 89 | public void setOriginalBytes(long originalBytes) { 90 | this.originalBytes = originalBytes; 91 | } 92 | 93 | public String getHost() { 94 | return host; 95 | } 96 | 97 | public void setHost(String host) { 98 | this.host = host; 99 | } 100 | 101 | public int getSplit() { 102 | return split; 103 | } 104 | 105 | public void setSplit(int split) { 106 | this.split = split; 107 | } 108 | 109 | public int getProgress() { 110 | return progress; 111 | } 112 | 113 | public void setProgress(int progress) { 114 | this.progress = progress; 115 | } 116 | 117 | public String getStatus() { 118 | return status; 119 | } 120 | 121 | public void setStatus(String status) { 122 | this.status = status; 123 | } 124 | 125 | public String getAdded() { 126 | return added; 127 | } 128 | 129 | public void setAdded(String added) { 130 | this.added = added; 131 | } 132 | 133 | public List getFiles() { 134 | return files; 135 | } 136 | 137 | public void setFiles(List files) { 138 | this.files = files; 139 | } 140 | 141 | public List getLinks() { 142 | return links; 143 | } 144 | 145 | public void setLinks(List links) { 146 | this.links = links; 147 | } 148 | 149 | public String getEnded() { 150 | return ended; 151 | } 152 | 153 | public void setEnded(String ended) { 154 | this.ended = ended; 155 | } 156 | 157 | public int getSpeed() { 158 | return speed; 159 | } 160 | 161 | public void setSpeed(int speed) { 162 | this.speed = speed; 163 | } 164 | 165 | public int getSeeders() { 166 | return seeders; 167 | } 168 | 169 | public void setSeeders(int seeders) { 170 | this.seeders = seeders; 171 | } 172 | 173 | public TorrentInfoDTO(){ 174 | 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/main/java/com/stream/realdebrid/dtos/UnrestrictDTO.java: -------------------------------------------------------------------------------- 1 | package com.stream.realdebrid.dtos; 2 | 3 | public class UnrestrictDTO { 4 | 5 | private String id; 6 | private String filename; 7 | private String mimetype; 8 | private long filesize; 9 | private String link; 10 | private String host; 11 | private int chunks; 12 | private int crc; 13 | private String download; 14 | private int streamable; 15 | 16 | public String getId() { 17 | return id; 18 | } 19 | 20 | public void setId(String id) { 21 | this.id = id; 22 | } 23 | 24 | public String getFilename() { 25 | return filename; 26 | } 27 | 28 | public void setFilename(String filename) { 29 | this.filename = filename; 30 | } 31 | 32 | public String getMimetype() { 33 | return mimetype; 34 | } 35 | 36 | public void setMimetype(String mimetype) { 37 | this.mimetype = mimetype; 38 | } 39 | 40 | public long getFilesize() { 41 | return filesize; 42 | } 43 | 44 | public void setFilesize(long filesize) { 45 | this.filesize = filesize; 46 | } 47 | 48 | public String getLink() { 49 | return link; 50 | } 51 | 52 | public void setLink(String link) { 53 | this.link = link; 54 | } 55 | 56 | public String getHost() { 57 | return host; 58 | } 59 | 60 | public void setHost(String host) { 61 | this.host = host; 62 | } 63 | 64 | public int getChunks() { 65 | return chunks; 66 | } 67 | 68 | public void setChunks(int chunks) { 69 | this.chunks = chunks; 70 | } 71 | 72 | public int getCrc() { 73 | return crc; 74 | } 75 | 76 | public void setCrc(int crc) { 77 | this.crc = crc; 78 | } 79 | 80 | public String getDownload() { 81 | return download; 82 | } 83 | 84 | public void setDownload(String download) { 85 | this.download = download; 86 | } 87 | 88 | public int getStreamable() { 89 | return streamable; 90 | } 91 | 92 | public void setStreamable(int streamable) { 93 | this.streamable = streamable; 94 | } 95 | 96 | @Override 97 | public String toString() { 98 | return "UnrestrictDTO{" + 99 | "id='" + id + '\'' + 100 | ", filename='" + filename + '\'' + 101 | ", mimetype='" + mimetype + '\'' + 102 | ", filesize=" + filesize + 103 | ", link='" + link + '\'' + 104 | ", host='" + host + '\'' + 105 | ", chunks=" + chunks + 106 | ", crc=" + crc + 107 | ", downloads='" + download + '\'' + 108 | ", streamable=" + streamable + 109 | '}'; 110 | } 111 | 112 | public UnrestrictDTO(){ 113 | 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/main/java/com/stream/subtitles/DownloadTask.java: -------------------------------------------------------------------------------- 1 | package com.stream.subtitles; 2 | 3 | import com.stream.common.CommonConstants; 4 | import com.stream.common.CommonUtils; 5 | import com.stream.exceptions.ConnectionException; 6 | import org.jsoup.nodes.Document; 7 | 8 | import java.io.*; 9 | import java.net.URL; 10 | import java.nio.channels.Channels; 11 | import java.nio.channels.ReadableByteChannel; 12 | import java.util.logging.Logger; 13 | import java.util.zip.ZipEntry; 14 | import java.util.zip.ZipInputStream; 15 | 16 | public class DownloadTask implements Runnable { 17 | 18 | private final String link; 19 | private final String fileName; 20 | private final String imdbID; 21 | private static Logger logger = Logger.getLogger(DownloadTask.class.getName()); 22 | 23 | 24 | @Override 25 | public void run() { 26 | String url; 27 | try { 28 | url = getDownloadLinkFromYS(link); 29 | downloadSubtitle(url, fileName); 30 | String filePath = CommonConstants.SUBS_COMPRESSED_FOLDER + imdbID + CommonConstants.FORWARD_SLASH +fileName + CommonConstants.ZIP_EXTENSION; 31 | unzip(filePath, CommonConstants.SUBS_UNCOMPRESSED_FOLDER+imdbID); 32 | deleteFile(CommonConstants.SUBS_COMPRESSED_FOLDER + imdbID + CommonConstants.FORWARD_SLASH + fileName + CommonConstants.ZIP_EXTENSION); 33 | } catch (IOException | ConnectionException e) { 34 | logger.warning(e.getMessage()); 35 | } 36 | } 37 | 38 | private void deleteFile(String path) throws ConnectionException { 39 | File file = new File(path); 40 | if(file.exists() && !file.delete()) { 41 | throw new ConnectionException("Unable to delete subtitle file"); 42 | } 43 | } 44 | 45 | private String getDownloadLinkFromYS(String url) throws IOException { 46 | Document document1 = YtsSubtitleUtils.getScrapDataFromUrl(url); 47 | return document1.select(CommonConstants.SUBS_DOWNLOAD_SELECTOR).first().absUrl(CommonConstants.SELECTOR_HREF); 48 | } 49 | 50 | private void downloadSubtitle(String url, String fileName) throws IOException { 51 | String filePath = CommonConstants.SUBS_COMPRESSED_FOLDER + imdbID + CommonConstants.FORWARD_SLASH +fileName + CommonConstants.ZIP_EXTENSION; 52 | URL website = new URL(url); 53 | ReadableByteChannel rbc = Channels.newChannel(CommonUtils.getHttpUrlConnection(website, null).getInputStream()); 54 | FileOutputStream fos = new FileOutputStream(filePath); 55 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); 56 | } 57 | 58 | private void unzip(String zipFilePath, String destDir) { 59 | File dir = new File(destDir); 60 | if(!dir.exists()) { 61 | dir.mkdirs(); 62 | } 63 | FileInputStream fis; 64 | byte[] buffer = new byte[1024]; 65 | try { 66 | fis = new FileInputStream(zipFilePath); 67 | ZipInputStream zis = new ZipInputStream(fis); 68 | ZipEntry zipEntry = zis.getNextEntry(); 69 | while(zipEntry != null){ 70 | String filename = this.fileName + CommonConstants.SRT_EXTENSION; 71 | File newFile = new File(destDir + File.separator + filename); 72 | new File(newFile.getParent()).mkdirs(); 73 | FileOutputStream fos = new FileOutputStream(newFile); 74 | int len; 75 | while ((len = zis.read(buffer)) > 0) { 76 | fos.write(buffer, 0, len); 77 | } 78 | fos.close(); 79 | zis.closeEntry(); 80 | zipEntry = zis.getNextEntry(); 81 | } 82 | zis.closeEntry(); 83 | zis.close(); 84 | fis.close(); 85 | } catch (IOException e) { 86 | logger.warning(e.getMessage()); 87 | } 88 | 89 | } 90 | 91 | public DownloadTask(String link, String fileName, String imdbID){ 92 | this.link = link; 93 | this.fileName = fileName; 94 | this.imdbID = imdbID; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/com/stream/subtitles/YtsSubtitleUtils.java: -------------------------------------------------------------------------------- 1 | package com.stream.subtitles; 2 | import com.stream.common.CommonConstants; 3 | import com.stream.exceptions.BadTypeException; 4 | import com.stream.fetcher.FetcherUtils; 5 | import com.stream.fetcher.SourceDTO; 6 | import com.stream.fetcher.SourceUtils; 7 | import org.apache.commons.io.FileUtils; 8 | import org.jsoup.Connection; 9 | import org.jsoup.Jsoup; 10 | import org.jsoup.nodes.Document; 11 | 12 | import java.io.*; 13 | import java.util.List; 14 | import java.util.Map; 15 | import java.util.concurrent.ExecutorService; 16 | import java.util.concurrent.Executors; 17 | import java.util.concurrent.TimeUnit; 18 | import java.util.logging.Logger; 19 | import java.util.stream.Collectors; 20 | 21 | public final class YtsSubtitleUtils { 22 | 23 | private static Logger logger = Logger.getLogger(YtsSubtitleUtils.class.getName()); 24 | 25 | public static Document getScrapDataFromUrl(String url) throws IOException { 26 | Connection.Response response = Jsoup.connect(url) 27 | .ignoreContentType(true) 28 | .userAgent(CommonConstants.USER_AGENT_DETAILS) 29 | .referrer(CommonConstants.HTTP_REFERRER) 30 | .timeout(60_000) 31 | .followRedirects(true) 32 | .execute(); 33 | return response.parse(); 34 | } 35 | 36 | private static List> filterSubs(List> subs, String language) { 37 | return subs.stream().filter(a -> a.get(CommonConstants.SUBS_LANGUAGE).equals(language)).collect(Collectors.toList()); 38 | } 39 | 40 | public static void addSubtitleFromImdbId(String imdbId) throws BadTypeException, InterruptedException { 41 | try { 42 | SourceDTO torrentSourceDTO = FetcherUtils.loadSourceFromJson(CommonConstants.YTS_SUBS_SOURCE); 43 | List> searchResult = SourceUtils.getDataFromSource(torrentSourceDTO, imdbId); 44 | List> filterredList = YtsSubtitleUtils.filterSubs(searchResult, CommonConstants.SUBS_ENGLISH); 45 | ExecutorService pool = Executors.newFixedThreadPool(10); 46 | FileUtils.deleteDirectory(new File(CommonConstants.SUBS_ROOT_FOLDER)); 47 | new File(CommonConstants.SUBS_COMPRESSED_FOLDER + imdbId).mkdirs(); 48 | for (int i = 0; i< (Math.min(filterredList.size(), 5)); i++) { 49 | pool.submit(new DownloadTask(filterredList.get(i).get(CommonConstants.SUBS_DOWNLOAD_LINK), imdbId + CommonConstants.SYMBOL_HYPHEN+ i, imdbId)); 50 | } 51 | pool.shutdown(); 52 | pool.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS); 53 | } catch (IOException e) { 54 | logger.warning(e.getMessage()); 55 | } 56 | } 57 | 58 | private YtsSubtitleUtils(){ 59 | 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/stream/ytstorrent/YtsTorrentUtils.java: -------------------------------------------------------------------------------- 1 | package com.stream.ytstorrent; 2 | import com.fasterxml.jackson.core.type.TypeReference; 3 | import com.fasterxml.jackson.databind.DeserializationFeature; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.fasterxml.jackson.databind.PropertyNamingStrategy; 6 | import com.stream.common.CommonConstants; 7 | import com.stream.common.CommonUtils; 8 | import com.stream.exceptions.ConnectionException; 9 | import com.stream.exceptions.ItemNotFoundException; 10 | import com.stream.realdebrid.DebridUtils; 11 | import com.stream.ytstorrent.dtos.YtsMovieDTO; 12 | import com.stream.ytstorrent.dtos.YtsTorrentDTO; 13 | import org.json.JSONArray; 14 | import org.json.JSONObject; 15 | import java.io.IOException; 16 | import java.net.HttpURLConnection; 17 | import java.net.URL; 18 | import java.util.List; 19 | import java.util.Map; 20 | import java.util.concurrent.atomic.AtomicInteger; 21 | import java.util.logging.Logger; 22 | import java.util.stream.Collectors; 23 | 24 | public final class YtsTorrentUtils { 25 | private static Logger logger = Logger.getLogger(YtsTorrentUtils.class.getName()); 26 | 27 | public static String magnetGenerator(String torrentHash, String urlEncodedMovieName) { 28 | return CommonConstants.MAGNET_URL + torrentHash + CommonConstants.MAGNET_DOWNLOAD + urlEncodedMovieName + CommonConstants.MAGNET_TRACKER; 29 | } 30 | 31 | private static List getMovieDataDtoFromJson(String jsonString) throws ItemNotFoundException { 32 | JSONObject data = (JSONObject) new JSONObject(jsonString).get(CommonConstants.DATA); 33 | if(!data.has(CommonConstants.MOVIES)) { 34 | throw new ItemNotFoundException("NO RESULT FOUND"); 35 | } 36 | JSONArray arr = data.getJSONArray(CommonConstants.MOVIES); 37 | ObjectMapper objectMapper = new ObjectMapper(); 38 | objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); 39 | objectMapper.configure( 40 | DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); 41 | List dtos = null; 42 | 43 | try { 44 | dtos = objectMapper.readValue(arr.toString(), new TypeReference>() { 45 | }); 46 | } catch (IOException e) { 47 | logger.warning(e.getMessage()); 48 | } 49 | return dtos; 50 | } 51 | 52 | private static Integer askUserOption() { 53 | CommonUtils.print(CommonConstants.USER_INPUT_SELECT_OPTION); 54 | return Integer.parseInt(CommonUtils.read()); 55 | } 56 | 57 | private static List ytsTorrentSearch(String searchQuery) throws ItemNotFoundException { 58 | String jsonString; 59 | List dtos = null; 60 | 61 | try { 62 | URL url = new URL(CommonConstants.LIST_MOVIRES_YTS_URL + CommonConstants.SEARCH_QUERY_YTS + searchQuery); 63 | HttpURLConnection conn = CommonUtils.getHttpUrlConnection(url, null); 64 | if (conn.getResponseCode() == 200) { 65 | jsonString = CommonUtils.readJSON(conn.getInputStream()); 66 | dtos = YtsTorrentUtils.getMovieDataDtoFromJson(jsonString); 67 | } else { 68 | throw new ConnectionException("HttpResponseCode: " + conn.getResponseCode()); 69 | } 70 | } catch (IOException | ConnectionException e) { 71 | logger.warning(e.getMessage()); 72 | } 73 | return dtos; 74 | } 75 | 76 | public static YtsMovieDTO searchAndSelectMovie(String searchQuery) throws IOException, ItemNotFoundException, ConnectionException { 77 | YtsMovieDTO selectedMovie = null; 78 | if (searchQuery != null) { 79 | List movieDTOS = YtsTorrentUtils.ytsTorrentSearch(searchQuery); 80 | DebridUtils.removeNonInstantlyAvailableTorrents(movieDTOS); 81 | if (movieDTOS != null) { 82 | selectedMovie = selectMovie(movieDTOS); 83 | } 84 | } 85 | return selectedMovie; 86 | } 87 | 88 | private static YtsMovieDTO selectMovie(List movieDTOS) { 89 | YtsMovieDTO selectedMovie = null; 90 | AtomicInteger integer = new AtomicInteger(1); 91 | final Map mapOfDtos = movieDTOS.stream().collect(Collectors.toMap(c -> integer.getAndIncrement(), c -> c)); 92 | mapOfDtos.keySet().forEach(c -> CommonUtils.print(c + CommonConstants.DOT_AND_SPACE + mapOfDtos.get(c).getTitleLong())); 93 | boolean isWrongSelection = true; 94 | while (isWrongSelection) { 95 | Integer selection = YtsTorrentUtils.askUserOption(); 96 | if (selection <= mapOfDtos.size()) { 97 | selectedMovie = mapOfDtos.get(selection); 98 | isWrongSelection = false; 99 | } 100 | } 101 | return selectedMovie; 102 | } 103 | 104 | public static YtsTorrentDTO selectQuality(YtsMovieDTO selectedMovie) { 105 | YtsTorrentDTO selectedTorrents = null; 106 | AtomicInteger integer = new AtomicInteger(1); 107 | final Map mapOfDtos = selectedMovie.getTorrents().stream().collect(Collectors.toMap(c -> integer.getAndIncrement(), c -> c)); 108 | mapOfDtos.keySet().forEach(c -> CommonUtils.print(c + CommonConstants.DOT_AND_SPACE + mapOfDtos.get(c).getQuality())); 109 | boolean isWrongSelection = true; 110 | while (isWrongSelection) { 111 | Integer selection = YtsTorrentUtils.askUserOption(); 112 | if (selection <= mapOfDtos.size()) { 113 | selectedTorrents = mapOfDtos.get(selection); 114 | isWrongSelection = false; 115 | } 116 | } 117 | return selectedTorrents; 118 | } 119 | 120 | private YtsTorrentUtils(){ 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/com/stream/ytstorrent/dtos/YtsMovieDTO.java: -------------------------------------------------------------------------------- 1 | package com.stream.ytstorrent.dtos; 2 | 3 | import java.util.List; 4 | import java.util.Objects; 5 | 6 | public class YtsMovieDTO { 7 | 8 | private Integer id; 9 | private String url; 10 | private String imdbCode; 11 | private String title; 12 | private String titleEnglish; 13 | private String titleLong; 14 | private String slug; 15 | private Integer year; 16 | private Float rating; 17 | private Integer runtime; 18 | private List genres; 19 | private String summary; 20 | private String descriptionFull; 21 | private String synopsis; 22 | private String ytTrailerCode; 23 | private String language; 24 | private String mpaRating; 25 | private List torrents; 26 | private String backgroundImage; 27 | private String backgroundImageOriginal; 28 | private String smallCoverImage; 29 | private String mediumCoverImage; 30 | private String largeCoverImage; 31 | private String state; 32 | private String dateUploaded; 33 | 34 | public String getDateUploaded() { 35 | return dateUploaded; 36 | } 37 | 38 | public void setDateUploaded(String dateUploaded) { 39 | this.dateUploaded = dateUploaded; 40 | } 41 | 42 | public String getState() { 43 | return state; 44 | } 45 | 46 | public void setState(String state) { 47 | this.state = state; 48 | } 49 | 50 | @Override 51 | public String toString() { 52 | return "MovieDTO{" + 53 | "id=" + id + 54 | ", url='" + url + '\'' + 55 | ", imdbCode='" + imdbCode + '\'' + 56 | ", title='" + title + '\'' + 57 | ", titleEnglish='" + titleEnglish + '\'' + 58 | ", titleLong='" + titleLong + '\'' + 59 | ", slug='" + slug + '\'' + 60 | ", year=" + year + 61 | ", rating=" + rating + 62 | ", runtime=" + runtime + 63 | ", generes=" + genres+ 64 | ", summary='" + summary + '\'' + 65 | ", descriptionFull='" + descriptionFull + '\'' + 66 | ", synopsis='" + synopsis + '\'' + 67 | ", ytTrailerCode='" + ytTrailerCode + '\'' + 68 | ", language='" + language + '\'' + 69 | ", mpaRating='" + mpaRating + '\'' + 70 | ", torrent=" + torrents + 71 | ", backgroundImage='" + backgroundImage + '\'' + 72 | ", backgroundImageOriginal='" + backgroundImageOriginal + '\'' + 73 | ", smallCoverImage='" + smallCoverImage + '\'' + 74 | ", mediumCoverImage='" + mediumCoverImage + '\'' + 75 | ", largeCoverImage='" + largeCoverImage + '\'' + 76 | '}'; 77 | } 78 | 79 | @Override 80 | public boolean equals(Object o) { 81 | if (this == o) { 82 | return true; 83 | } 84 | if (o == null || getClass() != o.getClass()) { 85 | return false; 86 | } 87 | YtsMovieDTO movieDTO = (YtsMovieDTO) o; 88 | return Objects.equals(id, movieDTO.id) && 89 | Objects.equals(url, movieDTO.url) && 90 | Objects.equals(imdbCode, movieDTO.imdbCode) && 91 | Objects.equals(title, movieDTO.title) && 92 | Objects.equals(titleEnglish, movieDTO.titleEnglish) && 93 | Objects.equals(titleLong, movieDTO.titleLong) && 94 | Objects.equals(slug, movieDTO.slug) && 95 | Objects.equals(year, movieDTO.year) && 96 | Objects.equals(rating, movieDTO.rating) && 97 | Objects.equals(runtime, movieDTO.runtime) && 98 | Objects.equals(genres, movieDTO.genres) && 99 | Objects.equals(summary, movieDTO.summary) && 100 | Objects.equals(descriptionFull, movieDTO.descriptionFull) && 101 | Objects.equals(synopsis, movieDTO.synopsis) && 102 | Objects.equals(ytTrailerCode, movieDTO.ytTrailerCode) && 103 | Objects.equals(language, movieDTO.language) && 104 | Objects.equals(mpaRating, movieDTO.mpaRating) && 105 | Objects.equals(torrents, movieDTO.torrents) && 106 | Objects.equals(backgroundImage, movieDTO.backgroundImage) && 107 | Objects.equals(backgroundImageOriginal, movieDTO.backgroundImageOriginal) && 108 | Objects.equals(smallCoverImage, movieDTO.smallCoverImage) && 109 | Objects.equals(mediumCoverImage, movieDTO.mediumCoverImage) && 110 | Objects.equals(largeCoverImage, movieDTO.largeCoverImage) && 111 | Objects.equals(state, movieDTO.state) && 112 | Objects.equals(dateUploaded, movieDTO.dateUploaded); 113 | } 114 | 115 | @Override 116 | public int hashCode() { 117 | return Objects.hash(id, url, imdbCode, title, titleEnglish, titleLong, slug, year, rating, runtime, genres, summary, descriptionFull, synopsis, ytTrailerCode, language, mpaRating, torrents, backgroundImage, backgroundImageOriginal, smallCoverImage, mediumCoverImage, largeCoverImage, state, dateUploaded); 118 | } 119 | 120 | public String getBackgroundImage() { 121 | return backgroundImage; 122 | } 123 | 124 | public void setBackgroundImage(String backgroundImage) { 125 | this.backgroundImage = backgroundImage; 126 | } 127 | 128 | public String getBackgroundImageOriginal() { 129 | return backgroundImageOriginal; 130 | } 131 | 132 | public void setBackgroundImageOriginal(String backgroundImageOriginal) { 133 | this.backgroundImageOriginal = backgroundImageOriginal; 134 | } 135 | 136 | public String getSmallCoverImage() { 137 | return smallCoverImage; 138 | } 139 | 140 | public void setSmallCoverImage(String smallCoverImage) { 141 | this.smallCoverImage = smallCoverImage; 142 | } 143 | 144 | public String getMediumCoverImage() { 145 | return mediumCoverImage; 146 | } 147 | 148 | public void setMediumCoverImage(String mediumCoverImage) { 149 | this.mediumCoverImage = mediumCoverImage; 150 | } 151 | 152 | public String getLargeCoverImage() { 153 | return largeCoverImage; 154 | } 155 | 156 | public void setLargeCoverImage(String largeCoverImage) { 157 | this.largeCoverImage = largeCoverImage; 158 | } 159 | 160 | public Integer getId() { 161 | return id; 162 | } 163 | 164 | public void setId(Integer id) { 165 | this.id = id; 166 | } 167 | 168 | public String getUrl() { 169 | return url; 170 | } 171 | 172 | public void setUrl(String url) { 173 | this.url = url; 174 | } 175 | 176 | public String getImdbCode() { 177 | return imdbCode; 178 | } 179 | 180 | public void setImdbCode(String imdbCode) { 181 | this.imdbCode = imdbCode; 182 | } 183 | 184 | public String getTitle() { 185 | return title; 186 | } 187 | 188 | public void setTitle(String title) { 189 | this.title = title; 190 | } 191 | 192 | public String getTitleEnglish() { 193 | return titleEnglish; 194 | } 195 | 196 | public void setTitleEnglish(String titleEnglish) { 197 | this.titleEnglish = titleEnglish; 198 | } 199 | 200 | public String getTitleLong() { 201 | return titleLong; 202 | } 203 | 204 | public void setTitleLong(String titleLong) { 205 | this.titleLong = titleLong; 206 | } 207 | 208 | public String getSlug() { 209 | return slug; 210 | } 211 | 212 | public void setSlug(String slug) { 213 | this.slug = slug; 214 | } 215 | 216 | public Integer getYear() { 217 | return year; 218 | } 219 | 220 | public void setYear(Integer year) { 221 | this.year = year; 222 | } 223 | 224 | public Float getRating() { 225 | return rating; 226 | } 227 | 228 | public void setRating(Float rating) { 229 | this.rating = rating; 230 | } 231 | 232 | public Integer getRuntime() { 233 | return runtime; 234 | } 235 | 236 | public void setRuntime(Integer runtime) { 237 | this.runtime = runtime; 238 | } 239 | 240 | public List getGenres() { 241 | return genres; 242 | } 243 | 244 | public void setGenres(List genres) { 245 | this.genres = genres; 246 | } 247 | 248 | public String getSummary() { 249 | return summary; 250 | } 251 | 252 | public void setSummary(String summary) { 253 | this.summary = summary; 254 | } 255 | 256 | public String getDescriptionFull() { 257 | return descriptionFull; 258 | } 259 | 260 | public void setDescriptionFull(String descriptionFull) { 261 | this.descriptionFull = descriptionFull; 262 | } 263 | 264 | public String getSynopsis() { 265 | return synopsis; 266 | } 267 | 268 | public void setSynopsis(String synopsis) { 269 | this.synopsis = synopsis; 270 | } 271 | 272 | public String getYtTrailerCode() { 273 | return ytTrailerCode; 274 | } 275 | 276 | public void setYtTrailerCode(String ytTrailerCode) { 277 | this.ytTrailerCode = ytTrailerCode; 278 | } 279 | 280 | public String getLanguage() { 281 | return language; 282 | } 283 | 284 | public void setLanguage(String language) { 285 | this.language = language; 286 | } 287 | 288 | public String getMpaRating() { 289 | return mpaRating; 290 | } 291 | 292 | public void setMpaRating(String mpaRating) { 293 | this.mpaRating = mpaRating; 294 | } 295 | 296 | public List getTorrents() { 297 | return torrents; 298 | } 299 | 300 | public void setTorrents(List torrents) { 301 | this.torrents = torrents; 302 | } 303 | 304 | public YtsMovieDTO() { 305 | 306 | } 307 | } 308 | -------------------------------------------------------------------------------- /src/main/java/com/stream/ytstorrent/dtos/YtsTorrentDTO.java: -------------------------------------------------------------------------------- 1 | package com.stream.ytstorrent.dtos; 2 | 3 | import java.util.Objects; 4 | 5 | public class YtsTorrentDTO { 6 | 7 | private String url; 8 | private String hash; 9 | private String quality; 10 | private String type; 11 | private Integer seeds; 12 | private Integer peers; 13 | private String size; 14 | 15 | 16 | public String getUrl() { 17 | return url; 18 | } 19 | 20 | public void setUrl(String url) { 21 | this.url = url; 22 | } 23 | 24 | public String getHash() { 25 | return hash; 26 | } 27 | 28 | public void setHash(String hash) { 29 | this.hash = hash; 30 | } 31 | 32 | public String getQuality() { 33 | return quality; 34 | } 35 | 36 | public void setQuality(String quality) { 37 | this.quality = quality; 38 | } 39 | 40 | public String getType() { 41 | return type; 42 | } 43 | 44 | public void setType(String type) { 45 | this.type = type; 46 | } 47 | 48 | public Integer getSeeds() { 49 | return seeds; 50 | } 51 | 52 | public void setSeeds(Integer seeds) { 53 | this.seeds = seeds; 54 | } 55 | 56 | public Integer getPeers() { 57 | return peers; 58 | } 59 | 60 | public void setPeers(Integer peers) { 61 | this.peers = peers; 62 | } 63 | 64 | public String getSize() { 65 | return size; 66 | } 67 | 68 | public void setSize(String size) { 69 | this.size = size; 70 | } 71 | 72 | @Override 73 | public boolean equals(Object object) { 74 | if (this == object) { 75 | return true; 76 | } 77 | if (object == null || getClass() != object.getClass()) { 78 | return false; 79 | } 80 | YtsTorrentDTO that = (YtsTorrentDTO) object; 81 | return Objects.equals(url, that.url) && 82 | Objects.equals(hash, that.hash) && 83 | Objects.equals(quality, that.quality) && 84 | Objects.equals(type, that.type) && 85 | Objects.equals(seeds, that.seeds) && 86 | Objects.equals(peers, that.peers) && 87 | Objects.equals(size, that.size); 88 | } 89 | 90 | @Override 91 | public int hashCode() { 92 | return Objects.hash(url, hash, quality, type, seeds, peers, size); 93 | } 94 | 95 | @Override 96 | public String toString() { 97 | return "com.varub.torrent.TorrentDTO{" + 98 | "url='" + url + '\'' + 99 | ", hash='" + hash + '\'' + 100 | ", quality='" + quality + '\'' + 101 | ", type='" + type + '\'' + 102 | ", seeds='" + seeds + '\'' + 103 | ", peers='" + peers + '\'' + 104 | ", size='" + size + '\'' + 105 | '}'; 106 | } 107 | 108 | public static final class TorrentDTOBuilder { 109 | private String url; 110 | private String hash; 111 | private String quality; 112 | private String type; 113 | private Integer seeds; 114 | private Integer peers; 115 | private String size; 116 | 117 | private TorrentDTOBuilder() { 118 | } 119 | 120 | public static TorrentDTOBuilder aTorrentDTO() { 121 | return new TorrentDTOBuilder(); 122 | } 123 | 124 | public TorrentDTOBuilder withUrl(String url) { 125 | this.url = url; 126 | return this; 127 | } 128 | 129 | public TorrentDTOBuilder withHash(String hash) { 130 | this.hash = hash; 131 | return this; 132 | } 133 | 134 | public TorrentDTOBuilder withQuality(String quality) { 135 | this.quality = quality; 136 | return this; 137 | } 138 | 139 | public TorrentDTOBuilder withType(String type) { 140 | this.type = type; 141 | return this; 142 | } 143 | 144 | public TorrentDTOBuilder withSeeds(Integer seeds) { 145 | this.seeds = seeds; 146 | return this; 147 | } 148 | 149 | public TorrentDTOBuilder withPeers(Integer peers) { 150 | this.peers = peers; 151 | return this; 152 | } 153 | 154 | public TorrentDTOBuilder withSize(String size) { 155 | this.size = size; 156 | return this; 157 | } 158 | 159 | public YtsTorrentDTO build() { 160 | YtsTorrentDTO torrentDTO = new YtsTorrentDTO(); 161 | torrentDTO.setUrl(url); 162 | torrentDTO.setHash(hash); 163 | torrentDTO.setQuality(quality); 164 | torrentDTO.setType(type); 165 | torrentDTO.setSeeds(seeds); 166 | torrentDTO.setPeers(peers); 167 | torrentDTO.setSize(size); 168 | return torrentDTO; 169 | } 170 | } 171 | 172 | public YtsTorrentDTO(){ 173 | 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/main/resources/data/app-properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "vlc-path": "C:\\Program Files\\VideoLAN\\VLC\\vlc.exe", 3 | "vlc-fullscreen": "true", 4 | "vlc-aspect-ratio": "16:9", 5 | "subs-lang": "English" 6 | } -------------------------------------------------------------------------------- /src/main/resources/data/ytssubs.json: -------------------------------------------------------------------------------- 1 | { 2 | "sourceName": "ytssubs", 3 | "baseUrl": "https://yifysubtitles.org/movie-imdb/", 4 | "table": true, 5 | "tableIndex": 0, 6 | "tableContent": [ 7 | "rating", 8 | "language", 9 | "release", 10 | "other", 11 | "uploader" 12 | ], 13 | "additionalContent": [ 14 | "link" 15 | ], 16 | "additionalContentRelation": { 17 | "link": "release" 18 | }, 19 | "additionalContentReference": { 20 | "link": { 21 | "select": "a", 22 | "first": "", 23 | "absUrl": "href" 24 | } 25 | }, 26 | "headerSpecialCase": {}, 27 | "contentSpecialCase": 28 | { 29 | "release": { 30 | "select": "a", 31 | "text": "" 32 | } 33 | }, 34 | "nextPage": {} 35 | } --------------------------------------------------------------------------------