├── .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 | } --------------------------------------------------------------------------------