├── .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 |
35 |
36 |
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