├── .gitignore
├── Dockerfile
├── src
└── main
│ └── java
│ ├── META-INF
│ └── MANIFEST.MF
│ └── SubtitleDownloader.java
├── LICENSE
├── pom.xml
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | target/*
2 | out/*
3 | .idea/*
4 | subtitle-downloader.iml
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM openjdk:17
2 | WORKDIR /project
3 | COPY . .
4 | EXPOSE 8000
5 | CMD ["java", "-jar"]
6 |
--------------------------------------------------------------------------------
/src/main/java/META-INF/MANIFEST.MF:
--------------------------------------------------------------------------------
1 | Manifest-Version: 1.0
2 | Class-Path: jsoup-1.11.3.jar gson-2.8.5.jar
3 | Main-Class: SubtitleDownloader
4 |
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Syedalisait
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 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.subtitle
8 | subtitledownloader
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 | maven-assembly-plugin
22 |
23 |
24 |
25 | SubtitleDownloader
26 |
27 |
28 |
29 | jar-with-dependencies
30 |
31 | false
32 |
33 |
34 |
35 |
36 |
37 |
38 | org.jsoup
39 | jsoup
40 | 1.15.3
41 |
42 |
43 | com.google.code.gson
44 | gson
45 | 2.8.9
46 |
47 |
48 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Subtitle Downloader
2 | Subtitle Downloader is a Java Command Line tool to download subtitles for Movies in any language especially for Movies downloaded from Yify/YTS.
3 |
4 | Download link: [subtitle-downloader](https://github.com/syedalisait/subtitle-downloader/releases/tag/v1.0)
5 |
6 | Refer [Downloading Subtitles](#downloading-subtitles) section for help
7 |
8 | ## Table of Contents
9 | * [Overview](#overview)
10 | * [Pre-requisites](#pre-requisites)
11 | * [Verification](#verification)
12 | + [Windows](#windows)
13 | + [MAC/Linux](#mac-and-linux)
14 | * [Installation](#installation)
15 | * [Downloading Subtitles](#downloading-subtitles)
16 | + [Downloading Subtitles with Movie Name](#downloading-subtitles-with-movie-name)
17 | + [Downloading Subtitles with Movie File Path](#downloading-subtitles-with-movie-file-path)
18 | + [Downloading Subtitles with Movie Directory in Bulk](#downloading-subtitles-with-movie-directory-in-bulk)
19 | + [Downloading Subtitles with specific Language](#downloading-subtitles-with-specific-language)
20 | * [Working of Subtitle Downloader](#working-of-subtitle-downloader)
21 | + [Given a Movie Name](#given-a-movie-name)
22 | + [Given a File Path](#given-a-file-path)
23 | + [Given a Directory](#given-a-directory)
24 | * [Development Components](#development-components)
25 | * [Development Setup](#development-setup)
26 | * [Contribution](#contribution)
27 | * [Future Ideas](#future-ideas)
28 |
29 |
30 | ### Overview
31 | Downloading subtitles for movies is a repetitive task which involves the following steps
32 | 1. Search for the subtitles Online
33 | 2. Download the highest rated subtitle
34 | 3. Extract the downloaded zip/rar file
35 | 4. Open the Movie in a Media player and adding the Subtitle track
36 |
37 | The above process is a bit tedious.
38 |
39 | Subtitle Downloader solves this problem. All you have to do is give **one of the three options** below
40 | - Movie Name
41 | - Movie File Path/Location
42 | - Movies Directory Path where Multiple Movies are present inside the Directory
43 |
44 | You can also download subtitles for Movies in a specific language. By default downloads Subtitles in English
45 |
46 | ### Pre-requisites
47 | For this to work you need to install Java in your system
48 | Follow this [tutorial](https://www.guru99.com/install-java.html), it hardly takes 5 mins to set up and install Java
49 |
50 | ### Verification
51 | Once Java installation is complete, please do verify whether the installation is successful
52 |
53 | #### Windows
54 | - Press Windows + R button to open the Run window. Type cmd to open the Command-Line for Windows
55 | - Type `java -version` command to verify Java installation and its okay if the version number is different from what is shown below
56 | ```
57 | C:\Users\admin> java -version
58 | java version "1.8.0_191"
59 | Java(TM) SE Runtime Environment (build 1.8.0_191-b12)
60 | Java HotSpot(TM) 64-Bit Server VM (build 25.191-b12, mixed mode)
61 | ```
62 |
63 | - If there is an error like `java is not recognized as an internal or external command`, I would suggest you to set Environment Variable for `java` or give the entire path in cmd
64 | ```
65 | C:\Program Files\Java\jdk1.8.0_121\bin\java -version
66 | ```
67 |
68 | #### MAC and Linux
69 | - Open Terminal and type the command to verify
70 | ```
71 | $ java -version
72 | java version "1.8.0_191"
73 | Java(TM) SE Runtime Environment (build 1.8.0_191-b12)
74 | Java HotSpot(TM) 64-Bit Server VM (build 25.191-b12, mixed mode)
75 | ```
76 |
77 | ### Installation
78 | - Download the subtitle-downloader from [here](https://github.com/syedalisait/subtitle-downloader/releases/tag/v1.0)
79 | - Move the downloaded file to your preferred location where it is easy to access frequently
80 |
81 | ### Downloading Subtitles
82 |
83 | - Open Terminal/Cmd based on your Operating System (MAC/Windows/Unix)
84 | - Move to the location of the downloaded(`subtitle-downloader.jar`) jar file. This can usually be done using the `cd` command in most of the Operating System
85 | - Example: `cd C:/Users/Downloads` if this is the location where the `subtitle-downloader.jar` file was downloaded
86 | - Enter the command `java -jar subtitle-downloader.jar -help` to know how to download subtitles with one of the three options
87 |
88 |
89 |
90 | #### Downloading Subtitles with Movie Name
91 |
92 | - Open Terminal/Cmd and Move to the location where you need the subtitles for the Movie to be downloaded
93 | - Command:
94 | ```
95 | java -jar -m
96 | ```
97 |
98 | **Example:**
99 | - I need the subtitles downloaded inside directory `C:/Downloads/Movies/Inception`
100 |
101 | ```
102 | C:/Users/admin> cd C:/Downloads/Movies/Inception
103 | C:/Downloads/Movies/Inception> java -jar "C:/Users/downloads/subtitle-downloader.jar" -m "Inception"
104 | ```
105 |
106 |
107 |
108 | #### Downloading Subtitles with Movie File Path
109 | Command:
110 | ```
111 | java -jar -mP
112 | ```
113 | **Note: This will work fine only if the Movie Name has year appended to it**
114 |
115 | **Example:**
116 | - I need subtitles for the movie located here C:/Downloads/Movies/Prestige/Prestige.2010.bluray.mp4 (Year is present in the Movie Name)
117 |
118 | ```
119 | java -jar "C:/Users/downloads/subtitle-downloader.jar" -mP "C:/Downloads/Movies/Prestige/Prestige.2010.bluray.mp4"
120 | ```
121 |
122 |
123 |
124 | #### Downloading Subtitles with Movie Directory in Bulk
125 | Command:
126 | ```
127 | java -jar -mD
128 | ```
129 |
130 | **Note:**
131 | - **This will work fine only if the Movie Name has year appended to it**
132 | - **Each Movie(.mp4 file) should have its own folder/directory. This folder/directory should be inside a common directory which will be parsed (Refer Example to understand better)**
133 |
134 | **Example:**
135 | - I have a folder `C:/Downloads/Movies` where inside the directory there are multiple directories and each directory has a movie
136 | - Something like:
137 | - C:/Downloads/Movies/Inception/Inception.2010.bluray.mp4
138 | - C:/Downloads/Movies/Final Destination (2014)/Final Destination (2014) x264 Bluray.mp4
139 | - C:/Downloads/Movies/Prestige (2006)/Prestige.2006.x264.Bluray.[720p].mp4
140 | - To download subtitle for all these Movies:
141 | ```
142 | java -jar "C:/Users/downloads/subtitle-downloader.jar" -mD "C:/Downloads/Movies"
143 | ```
144 |
145 | #### Downloading Subtitles with specific Language
146 | - Can be used with other above commands with an extra parameter
147 | Command:
148 | ```
149 | java -jar -m -lang "French"
150 | ```
151 |
152 | ### Working of Subtitle Downloader
153 |
154 | #### Given a Movie Name
155 | - Searches for the movie in yifysubtitles.com
156 | - If the movie is found or if there are multiple movies found, returns the list and asks the user to select one of the movie
157 | - When the user selects the movie, downloads the highest rated subtitle for the movie in yifysubtitles.com
158 | - If there is a specific language that the user has provided, then searches for the highest rated subtitle for the specific language and downloads the movie
159 |
160 | #### Given a File Path
161 | - Parse the Movie File Path to guess the right movie name
162 | - Searches yifysubtitles.com to find out if any movie exists
163 | - If none were found, then hits Omdb Api to figure out the movie name
164 | - Still if it cannot find the results, asks the user to input the correct movie name
165 | - Then same logic as [above](#given-a-movie-name)
166 |
167 | #### Given a Directory
168 | - Performs the same logic as [Given a File Path](#given-a-file-path) for all the movies
169 | - Checks if the movie already has .srt file
170 | - If yes Skips downloading the Subtitle for that Movie
171 |
172 | ### Development Components
173 | - **Language:** Core Java
174 | - **Source of Subtitles:** Downloads Subtitles from yifysubtitles.com
175 | - **Movie Name:** [OmdbApi](https://www.omdbapi.com/) to get the correct Movie Name
176 | - **HTML Parser:** Using [Jsoup](https://jsoup.org/) to parse the contents of html
177 | - **JSON Parser:** Using [Gson](https://github.com/google/gson) to parse the Json
178 | - **Maven:** To manage dependencies (Gson and Jsoup)
179 |
180 | ### Development Setup
181 | - Fork the repository
182 | - Then Clone the repository which has been created in your github account from local
183 | ```
184 | git clone https://github.com/{USERNAME}/subtitle-downloader.git
185 | ```
186 | - Open the project in any IDE (My preference is IntelliJ) by selecting `File -> New -> Project From Existing Sources`
187 | - Select the **subtitle-downloader**
188 | - On next window select "Maven" and then complete the next next windows with default settings and Finish
189 | - Running the program needs 'Command Line Arguments' as parameters. So, do a `Run -> Edit with Configuration` and add appropriate 'Command Line Arguments' (Example: -m "Inception")
190 | - Right click SubtitleDownloader.java and Run the program as Java Application
191 | - Output will be displayed in the Console
192 |
193 | ### Contribution
194 | - Contributions are welcome even if its relatively simple in terms of Exception handling, Conversion of Java 7 to Java 8, improvisation of Logic and any other improvements that can be made to the tool
195 |
196 | ### Future Ideas
197 | - Searching Multiple sites for subtitles if its not present in yifysubtitles (Subscene, Opensubtitles etc.)
198 | - Transition from Command Line to GUI with JavaFX or Swing
199 |
--------------------------------------------------------------------------------
/src/main/java/SubtitleDownloader.java:
--------------------------------------------------------------------------------
1 | import com.google.gson.Gson;
2 | import com.google.gson.JsonObject;
3 | import org.jsoup.Jsoup;
4 | import org.jsoup.nodes.Document;
5 | import org.jsoup.nodes.Element;
6 | import org.jsoup.select.Elements;
7 |
8 | import java.io.*;
9 | import java.net.URL;
10 | import java.net.URLConnection;
11 | import java.util.Arrays;
12 | import java.util.List;
13 | import java.util.Optional;
14 | import java.util.Scanner;
15 | import java.util.concurrent.atomic.AtomicInteger;
16 | import java.util.regex.Matcher;
17 | import java.util.regex.Pattern;
18 | import java.util.zip.ZipEntry;
19 | import java.util.zip.ZipInputStream;
20 |
21 | public class SubtitleDownloader {
22 | private static String movieFolderPath = null;
23 | private static String movieFilePath = null;
24 | private static String language = "English";
25 | private static String movieName = null;
26 |
27 | private static void Usage() {
28 | System.out.println("\nUsage: ");
29 | System.out.println("------");
30 | System.out.println("\nCommand to get Subtitle with Movie Name: ");
31 | System.out.println("\tjava -jar -m ");
32 | System.out.println("\nExample:\n\tjava -jar \"C:/Users/admin/downloads/subtitle-downloader.jar\" -m \"Inception\"");
33 | System.out.println("\n\t\tOR");
34 | System.out.println("\nCommand to get Subtitles with Movie File Path: ");
35 | System.out.println("\tjava -jar -mP ");
36 | System.out.println("\nExample:\n\tjava -jar \"C:/Users/admin/downloads/subtitle-downloader.jar\" -mP \"E:/Movies/Final Destination/Final.Destination.2009.mp4\"");
37 | System.out.println("\n\t\tOR");
38 | System.out.println("\nCommand to get Subtitles for all the Movies in separate folders present inside a Folder:");
39 | System.out.println("\tjava -jar -mD ");
40 | System.out.println("\nExample:\n\tjava -jar \"C:/Users/admin/downloads/subtitle-downloader.jar\" -mD \"E:/Movies\"");
41 | System.out.println("\nOptional Parameters:");
42 | System.out.println("\t-lang Language of the Subtitle. By Default set to English");
43 | }
44 |
45 | private static void parseArguments(String[] arguments) {
46 | CommandOptions cmd = new CommandOptions(arguments);
47 | // Show Usage() if the user wants to know how to execute the program
48 | if (cmd.hasOption("-help")) {
49 | Usage();
50 | System.exit(0);
51 | }
52 |
53 | if (cmd.hasOptionAndValue("-m")) {
54 | movieName = cmd.valueOf("-m");
55 | }
56 | // If the user needs subtitle for a different language other than English
57 | if (cmd.hasOptionAndValue("-lang")) {
58 | language = cmd.valueOf("-lang");
59 | }
60 |
61 | // Get the movieFilePath or movieFolderPath to Download subtitles
62 | if (cmd.hasOptionAndValue("-mP")) {
63 | movieFilePath = cmd.valueOf("-mP");
64 | }
65 |
66 | if (cmd.hasOptionAndValue("-mD")) {
67 | movieFolderPath = cmd.valueOf("-mD");
68 | }
69 | }
70 |
71 | public static void main(String[] args) {
72 | // Parse Arguments of the Program
73 | parseArguments(args);
74 | if (movieName != null) {
75 | if (!getSubtitles(movieName, null)) {
76 | System.out.println("\nEXITING THE PROGRAM!!!");
77 | System.exit(1);
78 | }
79 | }
80 | else if (movieFilePath != null) {
81 | // Get MovieName using Full Path of Movie File Location
82 | System.out.println("Movie File Path:\n\t" + movieFilePath);
83 | String movieName = getMovieName(movieFilePath);
84 | // Call get Subtitles to download the subtitles to the movieFilePath location
85 | if(!getSubtitles(movieName, movieFilePath)) {
86 | System.out.println("\nEXITING THE PROGRAM!!!");
87 | System.exit(1);
88 | }
89 | }
90 | else if (movieFolderPath != null) {
91 | /*
92 | * Expectation:
93 | * movieFolderPath is something like E:/Movies or E:/Downloads/Movies where all the movies are present inside
94 | * separate folders
95 | * Example:
96 | * E:/Downloads/Movies/Thelma/Thelma (2017).mp4
97 | * E:/Downloads/Elle/Elle (2016).mp4
98 | * Logic:
99 | * 1) Parse through the directories inside "movieFolderPath"
100 | * 2) If there are any Movie Folders with ".srt" file present, Ignore them
101 | * 3) Else, download the subtitle for the movie
102 | */
103 |
104 | // Lambda Expression for FileNameFilter
105 | String[] files = new File(movieFolderPath).list((directory, name) -> getSubtitlesForAllMovies(directory, name));
106 | }
107 | else {
108 | System.out.println("Please provide one of the following: \n1) Movie Name\n2) Movie File Path\n3) Movie Folder Path");
109 | Usage();
110 | }
111 | }
112 |
113 | /**
114 | ***************************************** Util Functions *****************************************************************************
115 | * getSubtitleForAllMovies - Loops through the Movies Directory and calls getSubtitles function to Download the subtitles for the movies
116 | * getSubtitles - Given a movieName, Downloads the subtitle for the movie
117 | * getMovieName - Given the Full Path of Movie File location returns the Movie Name
118 | * getSearchResults - Given a Movie Name, returns the search results in Yify Subtitle
119 | * getMovieURL - Given a html content of "div class=media-body", gives the link to Movie URL page
120 | * getMovieAndYear - Given a html content of the Movie, parses the movieName and Year of the Movie
121 | * getSubtitleURL - Given a movieURL retrieves the Subtitle URL for the movie
122 | * getMovieFolder - Given a full path of the movie, returns the path of the parent folder
123 | * getHTMLContent - Given a URL, returns the Document which has the html content which can be used to parsed
124 | * downloadSubtitle - Given a downloadSubtitleURL and PathtoDownload, downloads the zip file, extracts the contents and
125 | saves the files in the given Path to Download
126 | * checkExtension - Given a File object and list of Extension, checks whether the File contains any of the extensions and returns a boolean
127 | * getExtensions - Returns the list of video file extensions
128 | * displayMovieAndYear - Given a html element and count variable -> Parses the movie name/year from the Html content and prints to console
129 | *****************************************************************************************************************************************
130 | **/
131 |
132 | private static boolean getSubtitlesForAllMovies(File directory, String name) {
133 | try {
134 | File temp = new File(directory, name);
135 | if (temp.isDirectory() && temp.listFiles() != null) {
136 | // Check if ".srt" file is already not present for the Movie
137 | if (Arrays.stream(temp.listFiles()).noneMatch(f -> f.toString().endsWith(".srt"))) {
138 | // Check and get the Movie File with one of the video format extension from the Movie Folder
139 | Optional file = Arrays.stream(temp.listFiles()).filter(f -> checkExtension(f, getFileExtensions())).findFirst();
140 | if (file.isPresent()) {
141 | String movieFilePath = file.get().toString();
142 | System.out.println("\nMovie File Path:\n\t" + movieFilePath);
143 | String movieName = getMovieName(movieFilePath);
144 | if(!getSubtitles(movieName, movieFilePath)) {
145 | System.out.println("\nSkipping Downloading Subtitles for the Current Movie");
146 | System.out.println("---------------------------------------------------------------" +
147 | "---------------------------------------------------\n");
148 | }
149 | }
150 | }
151 | }
152 | }
153 | catch (Exception e) {
154 | e.printStackTrace();
155 | System.out.println("Exception: " + e);
156 | }
157 | return false;
158 | }
159 |
160 | private static boolean getSubtitles(String movieName, String movieFilePath) {
161 | Scanner s = new Scanner(System.in);
162 | String movieUrl = null;
163 | boolean flag = true;
164 |
165 | if (movieName == null) {
166 | System.out.println("\nMovie doesn't exist in yifysubtitles.com\n\t\t( OR )\n\"Movie Name\" " +
167 | "which was parsed from Movie filepath gives empty result when searched\n" +
168 | "\nPlease enter a keyword/part of the Movie Name as in \"Harry\" in \"Harry Potter\" to search once more" +
169 | "\nThis can provide appropriate results" +
170 | "\nMovie name ( Press ENTER To SKIP ): ");
171 | movieName = s.nextLine().trim();
172 | // Sometimes User might want to skip entering the Movie Name and move on to Next Movie
173 | // So Pressing Enter should skip Downloading Subtitles for the Movie
174 | if (movieName.equals("")) {
175 | return false;
176 | }
177 | flag = false;
178 | }
179 | System.out.println("\nMovie: " + movieName);
180 |
181 | // HTML Content of all Movies matching the movie name
182 | Elements elements = getSearchResults(movieName);
183 |
184 | // If the result is empty, Ask the User to input the Movie name
185 | if (elements == null || elements.isEmpty()) {
186 | System.out.println("\nNo results were found for Movie \"" + movieName + "\" in yifysubtitles.com");
187 | // If the User was given an option to type the movie name, Don't prompt the user to Enter the Movie Name again
188 | if (!flag) {
189 | return false;
190 | }
191 | System.out.print("\nPlease enter a keyword/part of the Movie Name as in \"Harry\" in \"Harry Potter 3\" to search again" +
192 | "\nMovie name ( Press ENTER To SKIP ): ");
193 | movieName = s.nextLine().trim();
194 | if (movieName.equals("")) {
195 | return false;
196 | }
197 | elements = getSearchResults(movieName);
198 | if (elements == null || elements.isEmpty()) {
199 | System.out.println("Movie: " + movieName + " does not exists in yifysubtitles.com");
200 | return false;
201 | }
202 | }
203 |
204 | if(elements.size() == 1) {
205 | movieUrl = getMovieURL(elements.get(0));
206 | }
207 | else {
208 | System.out.println("\nMore than one result found for: " + movieName);
209 | AtomicInteger i = new AtomicInteger(0);
210 | elements.forEach(e -> displayMovieAndYear(e, i.incrementAndGet()));
211 | System.out.print("Please enter the number to select the movie to download subtitles ( PRESS 0 TO SKIP ): ");
212 | int option = s.nextInt();
213 | // Skip Processing the Current Movie
214 | if (option == 0) {
215 | return false;
216 | }
217 | if (option < 0 ||option > elements.size()) {
218 | System.out.println("\n[ERROR] - INVALID OPTION SELECTED: " + option);
219 | return false;
220 | }
221 | movieUrl = getMovieURL(elements.get(option - 1));
222 |
223 | // Update the Movie Name with the Option Selected
224 | movieName = getMovieAndYear(elements.get(option - 1));
225 | }
226 |
227 | String subtitleUrl = getSubtitleURL(movieUrl, language);
228 | if (subtitleUrl == null) {
229 | System.out.println("\nSubtitle doesn't exist for the Movie \"" + movieName + "\" in language \"" + language + "\" in yifysubtitles.com");
230 | System.out.println("URL: " + movieUrl);
231 | return false;
232 | }
233 | System.out.println("Movie URL: " + movieUrl);
234 | System.out.println("Subtitle URL: " + subtitleUrl);
235 |
236 | // Download subtitle URL is same as Subtitle URL except /subtitles changes to /subtitle with ".zip" appended
237 | String downloadSubtitleUrl = subtitleUrl.replace("/subtitles", "/subtitle") + ".zip";
238 |
239 | String movieFolder;
240 | // If there is no movieFilePath given, then take the Current Directory
241 | // as the place to download subtitles
242 | if (movieFilePath != null) {
243 | movieFolder = getMovieFolder(movieFilePath);
244 | }
245 | else {
246 | movieFolder = System.getProperty("user.dir");
247 | }
248 |
249 | if (movieFolder == null) {
250 | System.out.println("Something went wrong while trying to get Movie folder for Movie File Path:\n\t" + movieFilePath + "\n");
251 | return false;
252 | }
253 |
254 | // Download and Extract the zip to get the Subtitle
255 | downloadSubtitle(downloadSubtitleUrl, movieFolder);
256 | System.out.println("\nSuccessfully downloaded Subtitle for Movie: " + movieName + "\n");
257 | System.out.println("---------------------------------------------------------------" +
258 | "---------------------------------------------------\n");
259 | return true;
260 | }
261 |
262 | private static String getMovieName(String movieFilePath) {
263 | /*
264 | * Please use Regexr.com to if you like to understand the below regex precisely
265 | * This is one of the common regex to filter out "Movie name" from other contents present in the Name of the Movie
266 | * Example: E:/Movies/Final Destination/Final.Destination.2013.[720p].[AAA].Watever.mp4
267 | * This regex trims the name and gives "Final.Destination" as output
268 | * Important Assumption here is that every movie name has "Year" appended to it separated
269 | * either by a "." or "[" or " "(space,tabs), otherwise this regex won't work to get the correct movie name
270 | */
271 | Pattern movieFilePathPattern = Pattern.compile("(?:.*[\\\\\\/])?(.*)(?:[\\s\\.\\(\\[]\\d{4}[\\.\\)\\]\\s]).*");
272 | Matcher movieNameMatcher = movieFilePathPattern.matcher(movieFilePath);
273 | if (!movieNameMatcher.find()) {
274 | return null;
275 | }
276 | // Replace '.' with space in the Control group which was selected
277 | String movieName = movieNameMatcher.group(1).replace(".", " ");
278 | // YIFY Subtitles Website URL
279 | String yifySubtitleUrl = "https://www.yifysubtitles.com/search?q=" + movieName.replace(" ", "+");
280 |
281 | try {
282 | // Check if the Yify subtitle site gives any results for the movie search
283 | // Or else hit OMDB API to retrieve the correct Movie Name
284 | Document document = Jsoup.connect(yifySubtitleUrl).get();
285 | Element element = document.select("div.container > div.row > div > div[style=\"text-align:center;\"]").first();
286 |
287 | // If the Yify subtitle site returns no results, hit the OMDB API to retrieve the movie name
288 | if (element != null && "no results".equals(element.text())) {
289 | // Hit OMDB API to retrieve the Movie name
290 | String OmdbUrl = "http://www.omdbapi.com/?apikey=d345b81e&t=" + movieName.replace(" ", "+");
291 | String omdbJsonData = Jsoup.connect(OmdbUrl).ignoreContentType(true).execute().body();
292 | JsonObject jsonObject = new Gson().fromJson(omdbJsonData, JsonObject.class);
293 | if (jsonObject == null || jsonObject.get("Title") == null) {
294 | return null;
295 | }
296 | System.out.println("\nNo results in yifysubtitles.com for Movie: " + movieName + "\n" + "Closest matching ");
297 | movieName = jsonObject.get("Title").getAsString();
298 | }
299 | }
300 | catch (IOException e) {
301 | System.out.println("Exception: " + e);
302 | e.printStackTrace();
303 | }
304 | return movieName;
305 | }
306 |
307 | private static Elements getSearchResults(String movieName) {
308 | final String yifySubtitleUrl = "https://www.yifysubtitles.com/search?q=" + movieName.replace(" ", "+");
309 | Document document = getHTMLContent(yifySubtitleUrl);
310 | if (document == null) {
311 | return null;
312 | }
313 | return document.select("div.media-body");
314 | }
315 |
316 | private static String getMovieURL(Element element) {
317 | String href = element.select("a").first().attr("href");
318 | return "https://www.yifysubtitles.com" + href;
319 | }
320 |
321 | private static String getMovieAndYear(Element element) {
322 | String movieName = element.select("h3.media-heading").text();
323 | String year = element.select("span.movinfo-section").first().text().substring(0, 4);
324 | return movieName + " " + year;
325 | }
326 |
327 | private static String getSubtitleURL(String movieUrl, String language) {
328 | // Get the HTML content of the page with list of subtitles in various languages
329 | Document document = getHTMLContent(movieUrl);
330 | if (document == null) {
331 | return null;
332 | }
333 | // Select all the subtitles in the page
334 | Elements elements = document.select("tr[data-id]");
335 |
336 | if (elements == null || elements.isEmpty()) {
337 | return null;
338 | }
339 |
340 | String subtitleUrl = null;
341 | /*
342 | * Loop through the tr[data-id] and check for the language
343 | * The first tr you find with language should be the highest rated subtitle for that language
344 | * as the tr displayed in the website is grouped by language and ordered by rating
345 | */
346 | for (Element element : elements) {
347 | if (element.select("td.flag-cell > span.sub-lang").text().contains(language)) {
348 | // Break after you find the highest rated subtitle
349 | subtitleUrl = element.select("td.download-cell > a").attr("href");
350 | break;
351 | }
352 | }
353 | subtitleUrl = (subtitleUrl == null) ? null : "https://www.yifysubtitles.com" + subtitleUrl;
354 | return subtitleUrl;
355 | }
356 |
357 | private static String getMovieFolder(String path) {
358 | if (path.lastIndexOf("/") != -1) {
359 | return path.substring(0, path.lastIndexOf("/"));
360 | }
361 | else if (path.lastIndexOf("\\") != -1) {
362 | return path.substring(0, path.lastIndexOf("\\"));
363 | }
364 | return null;
365 | }
366 |
367 | private static Document getHTMLContent(String url) {
368 | Document document = null;
369 | try {
370 | document = Jsoup.connect(url).get();
371 | }
372 | catch (IOException e) {
373 | System.out.println("Exception: " + e);
374 | e.printStackTrace();
375 | }
376 | return document;
377 | }
378 |
379 | private static void downloadSubtitle(String downloadSubtitleUrl, String movieFolder) {
380 | try {
381 | URL u = new URL(downloadSubtitleUrl);
382 | // Open Connection and get the contents of the zip file
383 | URLConnection connection = u.openConnection();
384 | InputStream in = connection.getInputStream();
385 | // Create a temporary zip file to where contents will be written
386 | File zipfile = File.createTempFile("temp", ".zip", new File(movieFolder));
387 | FileOutputStream fileOutputStream = new FileOutputStream(zipfile);
388 | byte[] bytes = new byte[1024];
389 | int count;
390 | // Write contents to temporary Zip file
391 | while ((count = in.read(bytes)) >= 0) {
392 | fileOutputStream.write(bytes, 0, count);
393 | }
394 | // Flush the contents from output stream
395 | fileOutputStream.flush();
396 |
397 | File dir = new File(movieFolder);
398 | if (!dir.exists()) {
399 | dir.mkdirs();
400 | }
401 | // Create a Input Stream to read contents of the temporary zip file
402 | FileInputStream fileInputStream = new FileInputStream(zipfile);
403 | ZipInputStream zis = new ZipInputStream(fileInputStream);
404 | ZipEntry ze = zis.getNextEntry();
405 | while (ze != null) {
406 | String filename = ze.getName();
407 | // Check for .srt file and ignore other files
408 | if (!filename.endsWith(".srt")) {
409 | ze = zis.getNextEntry();
410 | continue;
411 | }
412 | // Create a new file for the .srt file
413 | File newFile = new File(movieFolder, filename);
414 | //Create directories for sub directories in zip
415 | new File(newFile.getParent()).mkdirs();
416 | FileOutputStream fos = new FileOutputStream(newFile);
417 | // Write each zip file contents to the newly created file
418 | while ((count = zis.read(bytes)) >= 0) {
419 | fos.write(bytes, 0, count);
420 | }
421 | fos.close();
422 | zis.closeEntry();
423 | ze = zis.getNextEntry();
424 | }
425 | fileOutputStream.close();
426 | fileInputStream.close();
427 | in.close();
428 | // Delete the zip file
429 | zipfile.delete();
430 | }
431 | catch (Exception e) {
432 | System.out.println("Exception: " + e);
433 | e.printStackTrace();
434 | }
435 | }
436 |
437 | private static boolean checkExtension(File file, List fileExtensions) {
438 | int index = file.toString().lastIndexOf(".");
439 | // If its a directory and there is no extension
440 | if (index == -1) {
441 | return false;
442 | }
443 | String format = file.toString().substring(file.toString().lastIndexOf("."));
444 | return fileExtensions.contains(format);
445 | }
446 |
447 | private static List getFileExtensions() {
448 | return Arrays.asList(".avi", ".mp4", ".mkv", ".mpg", ".mpeg", ".mov", ".rm", ".vob", ".wmv", ".flv", ".3gp");
449 | }
450 |
451 | private static void displayMovieAndYear(Element element, int n) {
452 | String movieName = element.select("h3.media-heading").text();
453 | String year = element.select("span.movinfo-section").first().text().substring(0, 4);
454 | System.out.println(n + ". " + movieName + " " + year);
455 | }
456 |
457 |
458 | }
459 |
460 | class CommandOptions {
461 | private List arguments;
462 |
463 | // Constructor
464 | CommandOptions(String[] args) {
465 | arguments = Arrays.asList(args);
466 | }
467 |
468 | // Check whether the Option Exists
469 | boolean hasOption (String option) {
470 | return arguments.contains(option);
471 | }
472 |
473 | // Check whether the Option and Value Exists
474 | boolean hasOptionAndValue(String option) {
475 | return arguments.contains(option) && arguments.indexOf(option) + 1 < arguments.size();
476 | }
477 |
478 | // Return the Option's value if it exists
479 | String valueOf(String option) {
480 | if (arguments.indexOf(option) + 1 < arguments.size()) {
481 | return (String)arguments.get(arguments.indexOf(option) + 1);
482 | }
483 | else {
484 | return null;
485 | }
486 | }
487 | }
--------------------------------------------------------------------------------