Added base functionality, working prototype
This commit is contained in:
		
							parent
							
								
									9d4317ee9d
								
							
						
					
					
						commit
						34ea601a95
					
				
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					/target/
 | 
				
			||||||
							
								
								
									
										19
									
								
								nb-configuration.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								nb-configuration.xml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="UTF-8"?>
 | 
				
			||||||
 | 
					<project-shared-configuration>
 | 
				
			||||||
 | 
					    <!--
 | 
				
			||||||
 | 
					This file contains additional configuration written by modules in the NetBeans IDE.
 | 
				
			||||||
 | 
					The configuration is intended to be shared among all the users of project and
 | 
				
			||||||
 | 
					therefore it is assumed to be part of version control checkout.
 | 
				
			||||||
 | 
					Without this configuration present, some functionality in the IDE may be limited or fail altogether.
 | 
				
			||||||
 | 
					-->
 | 
				
			||||||
 | 
					    <properties xmlns="http://www.netbeans.org/ns/maven-properties-data/1">
 | 
				
			||||||
 | 
					        <!--
 | 
				
			||||||
 | 
					Properties that influence various parts of the IDE, especially code formatting and the like. 
 | 
				
			||||||
 | 
					You can copy and paste the single properties, into the pom.xml file and the IDE will pick them up.
 | 
				
			||||||
 | 
					That way multiple projects can share the same settings (useful for formatting rules for example).
 | 
				
			||||||
 | 
					Any value defined here will override the pom.xml file value but is only applicable to the current project.
 | 
				
			||||||
 | 
					-->
 | 
				
			||||||
 | 
					        <org-netbeans-modules-javascript2-requirejs.enabled>true</org-netbeans-modules-javascript2-requirejs.enabled>
 | 
				
			||||||
 | 
					        <netbeans.hint.jdkPlatform>JDK_16</netbeans.hint.jdkPlatform>
 | 
				
			||||||
 | 
					    </properties>
 | 
				
			||||||
 | 
					</project-shared-configuration>
 | 
				
			||||||
							
								
								
									
										17
									
								
								nbactions.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								nbactions.xml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="UTF-8"?>
 | 
				
			||||||
 | 
					<actions>
 | 
				
			||||||
 | 
					        <action>
 | 
				
			||||||
 | 
					            <actionName>run</actionName>
 | 
				
			||||||
 | 
					            <packagings>
 | 
				
			||||||
 | 
					                <packaging>jar</packaging>
 | 
				
			||||||
 | 
					            </packagings>
 | 
				
			||||||
 | 
					            <goals>
 | 
				
			||||||
 | 
					                <goal>process-classes</goal>
 | 
				
			||||||
 | 
					                <goal>org.codehaus.mojo:exec-maven-plugin:1.5.0:exec</goal>
 | 
				
			||||||
 | 
					            </goals>
 | 
				
			||||||
 | 
					            <properties>
 | 
				
			||||||
 | 
					                <exec.args>-classpath %classpath com.greinet.tvtotalripper.Main</exec.args>
 | 
				
			||||||
 | 
					                <exec.executable>java</exec.executable>
 | 
				
			||||||
 | 
					            </properties>
 | 
				
			||||||
 | 
					        </action>
 | 
				
			||||||
 | 
					    </actions>
 | 
				
			||||||
							
								
								
									
										27
									
								
								pom.xml
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								pom.xml
									
									
									
									
									
								
							@ -5,6 +5,33 @@
 | 
				
			|||||||
    <artifactId>TvTotalRipper</artifactId>
 | 
					    <artifactId>TvTotalRipper</artifactId>
 | 
				
			||||||
    <version>1.0</version>
 | 
					    <version>1.0</version>
 | 
				
			||||||
    <packaging>jar</packaging>
 | 
					    <packaging>jar</packaging>
 | 
				
			||||||
 | 
					    <dependencies>
 | 
				
			||||||
 | 
					        <dependency>
 | 
				
			||||||
 | 
					            <groupId>org.seleniumhq.selenium</groupId>
 | 
				
			||||||
 | 
					            <artifactId>selenium-java</artifactId>
 | 
				
			||||||
 | 
					            <version>3.141.59</version>
 | 
				
			||||||
 | 
					        </dependency>
 | 
				
			||||||
 | 
					        <dependency>
 | 
				
			||||||
 | 
					            <groupId>org.apache.logging.log4j</groupId>
 | 
				
			||||||
 | 
					            <artifactId>log4j-core</artifactId>
 | 
				
			||||||
 | 
					            <version>2.13.0</version>
 | 
				
			||||||
 | 
					        </dependency>
 | 
				
			||||||
 | 
					        <dependency>
 | 
				
			||||||
 | 
					            <groupId>commons-io</groupId>
 | 
				
			||||||
 | 
					            <artifactId>commons-io</artifactId>
 | 
				
			||||||
 | 
					            <version>2.8.0</version>
 | 
				
			||||||
 | 
					        </dependency>
 | 
				
			||||||
 | 
					        <dependency>
 | 
				
			||||||
 | 
					            <groupId>org.apache.logging.log4j</groupId>
 | 
				
			||||||
 | 
					            <artifactId>log4j-api</artifactId>
 | 
				
			||||||
 | 
					            <version>2.13.0</version>
 | 
				
			||||||
 | 
					        </dependency>
 | 
				
			||||||
 | 
					        <dependency>
 | 
				
			||||||
 | 
					            <groupId>org.mp4parser</groupId>
 | 
				
			||||||
 | 
					            <artifactId>isoparser</artifactId>
 | 
				
			||||||
 | 
					            <version>1.9.41</version>
 | 
				
			||||||
 | 
					        </dependency>
 | 
				
			||||||
 | 
					    </dependencies>
 | 
				
			||||||
    <properties>
 | 
					    <properties>
 | 
				
			||||||
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 | 
					        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 | 
				
			||||||
        <maven.compiler.source>1.8</maven.compiler.source>
 | 
					        <maven.compiler.source>1.8</maven.compiler.source>
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										
											BIN
										
									
								
								resources/chromedriver.exe
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								resources/chromedriver.exe
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										172
									
								
								src/main/java/com/greinet/tvtotalripper/Main.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										172
									
								
								src/main/java/com/greinet/tvtotalripper/Main.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,172 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * To change this license header, choose License Headers in Project Properties.
 | 
				
			||||||
 | 
					 * To change this template file, choose Tools | Templates
 | 
				
			||||||
 | 
					 * and open the template in the editor.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					package com.greinet.tvtotalripper;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.greinet.tvtotalripper.crawler.CrawlerUtil;
 | 
				
			||||||
 | 
					import java.io.File;
 | 
				
			||||||
 | 
					import java.io.FileInputStream;
 | 
				
			||||||
 | 
					import java.io.FileOutputStream;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.net.MalformedURLException;
 | 
				
			||||||
 | 
					import java.net.URL;
 | 
				
			||||||
 | 
					import java.nio.channels.Channels;
 | 
				
			||||||
 | 
					import java.nio.channels.ReadableByteChannel;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					import java.util.logging.Level;
 | 
				
			||||||
 | 
					import java.util.stream.Collectors;
 | 
				
			||||||
 | 
					import org.apache.logging.log4j.LogManager;
 | 
				
			||||||
 | 
					import org.apache.logging.log4j.Logger;
 | 
				
			||||||
 | 
					import org.mp4parser.IsoFile;
 | 
				
			||||||
 | 
					import org.mp4parser.boxes.apple.AppleNameBox;
 | 
				
			||||||
 | 
					import org.mp4parser.tools.Path;
 | 
				
			||||||
 | 
					import org.openqa.selenium.By;
 | 
				
			||||||
 | 
					import org.openqa.selenium.TimeoutException;
 | 
				
			||||||
 | 
					import org.openqa.selenium.WebDriver;
 | 
				
			||||||
 | 
					import org.openqa.selenium.WebElement;
 | 
				
			||||||
 | 
					import org.openqa.selenium.chrome.ChromeDriver;
 | 
				
			||||||
 | 
					import org.openqa.selenium.chrome.ChromeOptions;
 | 
				
			||||||
 | 
					import org.openqa.selenium.support.ui.ExpectedConditions;
 | 
				
			||||||
 | 
					import org.openqa.selenium.support.ui.WebDriverWait;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author agreiner
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class Main {
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private static final Logger logger = LogManager.getLogger(Main.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public static void main(String[] args) throws InterruptedException, IOException {
 | 
				
			||||||
 | 
					        //System.setProperty("webdriver.chrome.driver", "resources/chromedriver.exe");
 | 
				
			||||||
 | 
					        File f = new File("H:/Users/Andreas/Music/Bass/videoplayback.mp4");
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        MetaDataWriter mdp = new MetaDataWriter();
 | 
				
			||||||
 | 
					        mdp.writeMetadata(f.getAbsolutePath(), "Raab im Dschungel", "Stefan Raab", "TV Total", "TV Total vom 3.2.2021");
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        System.exit(0);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        System.out.println(CrawlerUtil.getFetchfileURL("https://www.myspass.de/shows/tvshows/tv-total/TV-total-Sendung-vom-08031999--/5716/"));
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        downloadFile(test());
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        ChromeOptions options = new ChromeOptions();
 | 
				
			||||||
 | 
					        options.addArguments("start-maximized");
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        WebDriver driver = new ChromeDriver(options);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        initialize(driver);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        List<WebElement> seriesElements = getSeriesElements(driver);
 | 
				
			||||||
 | 
					        //seriesElements.forEach(e -> System.out.println(e.getAttribute("href").replaceAll("https://www.myspass.de/shows/tvshows/", "")));
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        System.out.println("\nTV shows\n");
 | 
				
			||||||
 | 
					        // TV shows
 | 
				
			||||||
 | 
					        List<WebElement> tvshows = seriesElements.stream().filter(e -> e.getAttribute("href").contains("tvshows") || e.getAttribute("href").contains("UNKNOWN")).collect(Collectors.toList());
 | 
				
			||||||
 | 
					        tvshows.forEach(e -> System.out.println(e.getAttribute("href")));
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        System.out.println("\nWebshows\n");
 | 
				
			||||||
 | 
					        List<WebElement> webshows = seriesElements.stream().filter(e -> e.getAttribute("href").contains("webshows")).collect(Collectors.toList());
 | 
				
			||||||
 | 
					        webshows.forEach(e -> System.out.println(e.getAttribute("href")));
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        tvshows.get(3).click();
 | 
				
			||||||
 | 
					        Thread.sleep(3000);
 | 
				
			||||||
 | 
					        navigateToEpisodeOverview(driver);
 | 
				
			||||||
 | 
					        Thread.sleep(3000);
 | 
				
			||||||
 | 
					        getSeasonElements(driver).forEach(e -> System.out.println(e.getText()));
 | 
				
			||||||
 | 
					        Thread.sleep(3000);
 | 
				
			||||||
 | 
					        System.out.println("-------------------------------------------------------------------------------------------------------------------------------------");
 | 
				
			||||||
 | 
					        getEpisodeElements(driver).forEach(e -> System.out.println(e.getAttribute("href")));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private static boolean clickWhenClickable(WebDriver driver, By by, int timeout){
 | 
				
			||||||
 | 
					        WebDriverWait wait = new WebDriverWait(driver, timeout);
 | 
				
			||||||
 | 
					        try{
 | 
				
			||||||
 | 
					            WebElement acceptCookiesButton = wait.until(ExpectedConditions.elementToBeClickable(by));
 | 
				
			||||||
 | 
					            acceptCookiesButton.click();
 | 
				
			||||||
 | 
					        }catch(TimeoutException ex){
 | 
				
			||||||
 | 
					            logger.warn("Element represented by ["+by+"] not clickable.");
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private static void initialize(WebDriver driver){
 | 
				
			||||||
 | 
					        //Load start page
 | 
				
			||||||
 | 
					        //String urlStartPage = "https://www.myspass.de/shows/tvshows/tv-total/#bob-subnavi";
 | 
				
			||||||
 | 
					        String urlStartPage = "https://www.myspass.de/sendungen-a-bis-z/";
 | 
				
			||||||
 | 
					        logger.info("Loading start page ["+urlStartPage+"].");
 | 
				
			||||||
 | 
					        driver.get(urlStartPage);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Accept cookies if needed
 | 
				
			||||||
 | 
					        logger.info("Accepting cookies.");
 | 
				
			||||||
 | 
					        boolean cookiesSuccess = clickWhenClickable(driver, By.id("cmpbntyestxt"),5);
 | 
				
			||||||
 | 
					        if(!cookiesSuccess){
 | 
				
			||||||
 | 
					            logger.info("No cookie popup present.");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private static boolean navigateToEpisodeOverview(WebDriver driver){
 | 
				
			||||||
 | 
					        logger.info("Navigating to episode overview.");
 | 
				
			||||||
 | 
					        return clickWhenClickable(driver, By.xpath("/html/body/div[4]/div[1]/div[2]/ul/li[2]/a"),5);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private static List<WebElement> getSeriesElements(WebDriver driver){
 | 
				
			||||||
 | 
					        return driver.findElements(By.xpath("/html/body/div[5]/div/div/div/div/a"));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private static List<WebElement> getSeasonElements(WebDriver driver){
 | 
				
			||||||
 | 
					        return driver.findElements(By.xpath("/html/body/div[4]/div[1]/div[3]/div[2]/div[1]/select/option"));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private static List<WebElement> getEpisodeElements(WebDriver driver){
 | 
				
			||||||
 | 
					        return driver.findElements(By.xpath("/html/body/div[4]/div[1]/div[3]/div[2]/div[4]/div/div/div/a"));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private static String test(){
 | 
				
			||||||
 | 
					        WebDriver driver = new ChromeDriver();
 | 
				
			||||||
 | 
					        driver.get("https://de.fetchfile.net/herunterladen-von-myspass/");
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        WebElement textbox = driver.findElement(By.id("videoPath"));
 | 
				
			||||||
 | 
					        textbox.sendKeys("https://www.myspass.de/shows/tvshows/tv-total/TV-total-Sendung-vom-05012015--/20674/");
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        WebElement dlButton = driver.findElement(By.id("home-submit"));
 | 
				
			||||||
 | 
					        dlButton.click();
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        WebDriverWait wait = new WebDriverWait(driver, 20);
 | 
				
			||||||
 | 
					        WebElement vidButton = wait.until(ExpectedConditions.elementToBeClickable(By.className("download-link")));
 | 
				
			||||||
 | 
					        return vidButton.getAttribute("href");
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private static void downloadFile(String urlString){
 | 
				
			||||||
 | 
					        try {        
 | 
				
			||||||
 | 
					            File f = new File("download.mp4");
 | 
				
			||||||
 | 
					            f.createNewFile();
 | 
				
			||||||
 | 
					            URL url = new URL(urlString);
 | 
				
			||||||
 | 
					            ReadableByteChannel readableByteChannel = Channels.newChannel(url.openStream());
 | 
				
			||||||
 | 
					            FileOutputStream fileOutputStream = new FileOutputStream(f);
 | 
				
			||||||
 | 
					            fileOutputStream.getChannel().transferFrom(readableByteChannel, 0, Long.MAX_VALUE);
 | 
				
			||||||
 | 
					        } catch (MalformedURLException ex) {
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        } catch (IOException ex) {
 | 
				
			||||||
 | 
					            java.util.logging.Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										189
									
								
								src/main/java/com/greinet/tvtotalripper/MetaDataParser.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										189
									
								
								src/main/java/com/greinet/tvtotalripper/MetaDataParser.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,189 @@
 | 
				
			|||||||
 | 
					package com.greinet.tvtotalripper;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.mp4parser.Box;
 | 
				
			||||||
 | 
					import org.mp4parser.Container;
 | 
				
			||||||
 | 
					import org.mp4parser.IsoFile;
 | 
				
			||||||
 | 
					import org.mp4parser.boxes.apple.AppleItemListBox;
 | 
				
			||||||
 | 
					import org.mp4parser.boxes.apple.AppleNameBox;
 | 
				
			||||||
 | 
					import org.mp4parser.boxes.iso14496.part12.*;
 | 
				
			||||||
 | 
					import org.mp4parser.tools.Path;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.*;
 | 
				
			||||||
 | 
					import java.nio.ByteBuffer;
 | 
				
			||||||
 | 
					import java.nio.channels.Channels;
 | 
				
			||||||
 | 
					import java.nio.channels.FileChannel;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Change metadata and make sure chunkoffsets are corrected.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class MetaDataParser {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public FileChannel splitFileAndInsert(File f, long pos, long length) throws IOException {
 | 
				
			||||||
 | 
					        FileChannel read = new RandomAccessFile(f, "r").getChannel();
 | 
				
			||||||
 | 
					        File tmp = File.createTempFile("ChangeMetaData", "splitFileAndInsert");
 | 
				
			||||||
 | 
					        FileChannel tmpWrite = new RandomAccessFile(tmp, "rw").getChannel();
 | 
				
			||||||
 | 
					        read.position(pos);
 | 
				
			||||||
 | 
					        tmpWrite.transferFrom(read, 0, read.size() - pos);
 | 
				
			||||||
 | 
					        read.close();
 | 
				
			||||||
 | 
					        FileChannel write = new RandomAccessFile(f, "rw").getChannel();
 | 
				
			||||||
 | 
					        write.position(pos + length);
 | 
				
			||||||
 | 
					        tmpWrite.position(0);
 | 
				
			||||||
 | 
					        long transferred = 0;
 | 
				
			||||||
 | 
					        while ((transferred += tmpWrite.transferTo(0, tmpWrite.size() - transferred, write)) != tmpWrite.size()) {
 | 
				
			||||||
 | 
					            System.out.println(transferred);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        System.out.println(transferred);
 | 
				
			||||||
 | 
					        tmpWrite.close();
 | 
				
			||||||
 | 
					        tmp.delete();
 | 
				
			||||||
 | 
					        return write;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private boolean needsOffsetCorrection(IsoFile isoFile) {
 | 
				
			||||||
 | 
					        if (Path.getPath(isoFile, "moov[0]/mvex[0]") != null) {
 | 
				
			||||||
 | 
					            // Fragmented files don't need a correction
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            // no correction needed if mdat is before moov as insert into moov want change the offsets of mdat
 | 
				
			||||||
 | 
					            for (Box box : isoFile.getBoxes()) {
 | 
				
			||||||
 | 
					                if ("moov".equals(box.getType())) {
 | 
				
			||||||
 | 
					                    return true;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if ("mdat".equals(box.getType())) {
 | 
				
			||||||
 | 
					                    return false;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            throw new RuntimeException("I need moov or mdat. Otherwise all this doesn't make sense");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeRandomMetadata(String videoFilePath, String title) throws IOException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        File videoFile = new File(videoFilePath);
 | 
				
			||||||
 | 
					        if (!videoFile.exists()) {
 | 
				
			||||||
 | 
					            throw new FileNotFoundException("File " + videoFilePath + " not exists");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!videoFile.canWrite()) {
 | 
				
			||||||
 | 
					            throw new IllegalStateException("No write permissions to file " + videoFilePath);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        IsoFile isoFile = new IsoFile(videoFilePath);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        MovieBox moov = isoFile.getBoxes(MovieBox.class).get(0);
 | 
				
			||||||
 | 
					        FreeBox freeBox = findFreeBox(moov);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        boolean correctOffset = needsOffsetCorrection(isoFile);
 | 
				
			||||||
 | 
					        long sizeBefore = moov.getSize();
 | 
				
			||||||
 | 
					        long offset = 0;
 | 
				
			||||||
 | 
					        for (Box box : isoFile.getBoxes()) {
 | 
				
			||||||
 | 
					            if ("moov".equals(box.getType())) {
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            offset += box.getSize();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Create structure or just navigate to Apple List Box.
 | 
				
			||||||
 | 
					        UserDataBox userDataBox;
 | 
				
			||||||
 | 
					        if ((userDataBox = Path.getPath(moov, "udta")) == null) {
 | 
				
			||||||
 | 
					            userDataBox = new UserDataBox();
 | 
				
			||||||
 | 
					            moov.addBox(userDataBox);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        MetaBox metaBox;
 | 
				
			||||||
 | 
					        if ((metaBox = Path.getPath(userDataBox, "meta")) == null) {
 | 
				
			||||||
 | 
					            metaBox = new MetaBox();
 | 
				
			||||||
 | 
					            HandlerBox hdlr = new HandlerBox();
 | 
				
			||||||
 | 
					            hdlr.setHandlerType("mdir");
 | 
				
			||||||
 | 
					            metaBox.addBox(hdlr);
 | 
				
			||||||
 | 
					            userDataBox.addBox(metaBox);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        AppleItemListBox ilst;
 | 
				
			||||||
 | 
					        if ((ilst = Path.getPath(metaBox, "ilst")) == null) {
 | 
				
			||||||
 | 
					            ilst = new AppleItemListBox();
 | 
				
			||||||
 | 
					            metaBox.addBox(ilst);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (freeBox == null) {
 | 
				
			||||||
 | 
					            freeBox = new FreeBox(128 * 1024);
 | 
				
			||||||
 | 
					            metaBox.addBox(freeBox);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        // Got Apple List Box
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        AppleNameBox nam;
 | 
				
			||||||
 | 
					        if ((nam = Path.getPath(ilst, "©nam")) == null) {
 | 
				
			||||||
 | 
					            nam = new AppleNameBox();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        nam.setDataCountry(0);
 | 
				
			||||||
 | 
					        nam.setDataLanguage(0);
 | 
				
			||||||
 | 
					        nam.setValue(title);
 | 
				
			||||||
 | 
					        ilst.addBox(nam);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        long sizeAfter = moov.getSize();
 | 
				
			||||||
 | 
					        long diff = sizeAfter - sizeBefore;
 | 
				
			||||||
 | 
					        // This is the difference of before/after
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // can we compensate by resizing a Free Box we have found?
 | 
				
			||||||
 | 
					        if (freeBox.getData().limit() > diff) {
 | 
				
			||||||
 | 
					            // either shrink or grow!
 | 
				
			||||||
 | 
					            freeBox.setData(ByteBuffer.allocate((int) (freeBox.getData().limit() - diff)));
 | 
				
			||||||
 | 
					            sizeAfter = moov.getSize();
 | 
				
			||||||
 | 
					            diff = sizeAfter - sizeBefore;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (correctOffset && diff != 0) {
 | 
				
			||||||
 | 
					            correctChunkOffsets(moov, diff);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        BetterByteArrayOutputStream baos = new BetterByteArrayOutputStream();
 | 
				
			||||||
 | 
					        moov.getBox(Channels.newChannel(baos));
 | 
				
			||||||
 | 
					        isoFile.close();
 | 
				
			||||||
 | 
					        FileChannel fc;
 | 
				
			||||||
 | 
					        if (diff != 0) {
 | 
				
			||||||
 | 
					            // this is not good: We have to insert bytes in the middle of the file
 | 
				
			||||||
 | 
					            // and this costs time as it requires re-writing most of the file's data
 | 
				
			||||||
 | 
					            fc = splitFileAndInsert(videoFile, offset, sizeAfter - sizeBefore);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            // simple overwrite of something with the file
 | 
				
			||||||
 | 
					            fc = new RandomAccessFile(videoFile, "rw").getChannel();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        fc.position(offset);
 | 
				
			||||||
 | 
					        fc.write(ByteBuffer.wrap(baos.getBuffer(), 0, baos.size()));
 | 
				
			||||||
 | 
					        fc.close();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    FreeBox findFreeBox(Container c) {
 | 
				
			||||||
 | 
					        for (Box box : c.getBoxes()) {
 | 
				
			||||||
 | 
					            System.err.println(box.getType());
 | 
				
			||||||
 | 
					            if (box instanceof FreeBox) {
 | 
				
			||||||
 | 
					                return (FreeBox) box;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (box instanceof Container) {
 | 
				
			||||||
 | 
					                FreeBox freeBox = findFreeBox((Container) box);
 | 
				
			||||||
 | 
					                if (freeBox != null) {
 | 
				
			||||||
 | 
					                    return freeBox;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void correctChunkOffsets(MovieBox movieBox, long correction) {
 | 
				
			||||||
 | 
					        List<ChunkOffsetBox> chunkOffsetBoxes = Path.getPaths((Box) movieBox, "trak/mdia[0]/minf[0]/stbl[0]/stco[0]");
 | 
				
			||||||
 | 
					        if (chunkOffsetBoxes.isEmpty()) {
 | 
				
			||||||
 | 
					            chunkOffsetBoxes = Path.getPaths((Box) movieBox, "trak/mdia[0]/minf[0]/stbl[0]/st64[0]");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        for (ChunkOffsetBox chunkOffsetBox : chunkOffsetBoxes) {
 | 
				
			||||||
 | 
					            long[] cOffsets = chunkOffsetBox.getChunkOffsets();
 | 
				
			||||||
 | 
					            for (int i = 0; i < cOffsets.length; i++) {
 | 
				
			||||||
 | 
					                cOffsets[i] += correction;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static class BetterByteArrayOutputStream extends ByteArrayOutputStream {
 | 
				
			||||||
 | 
					        byte[] getBuffer() {
 | 
				
			||||||
 | 
					            return buf;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										231
									
								
								src/main/java/com/greinet/tvtotalripper/MetaDataWriter.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										231
									
								
								src/main/java/com/greinet/tvtotalripper/MetaDataWriter.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,231 @@
 | 
				
			|||||||
 | 
					package com.greinet.tvtotalripper;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.mp4parser.Box;
 | 
				
			||||||
 | 
					import org.mp4parser.Container;
 | 
				
			||||||
 | 
					import org.mp4parser.IsoFile;
 | 
				
			||||||
 | 
					import org.mp4parser.boxes.apple.AppleItemListBox;
 | 
				
			||||||
 | 
					import org.mp4parser.boxes.apple.AppleNameBox;
 | 
				
			||||||
 | 
					import org.mp4parser.boxes.iso14496.part12.*;
 | 
				
			||||||
 | 
					import org.mp4parser.tools.Path;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.*;
 | 
				
			||||||
 | 
					import java.nio.ByteBuffer;
 | 
				
			||||||
 | 
					import java.nio.channels.Channels;
 | 
				
			||||||
 | 
					import java.nio.channels.FileChannel;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					import org.mp4parser.boxes.apple.AppleArtistBox;
 | 
				
			||||||
 | 
					import org.mp4parser.boxes.apple.AppleTVEpisodeBox;
 | 
				
			||||||
 | 
					import org.mp4parser.boxes.apple.AppleTVEpisodeNumberBox;
 | 
				
			||||||
 | 
					import org.mp4parser.boxes.apple.AppleTVSeasonBox;
 | 
				
			||||||
 | 
					import org.mp4parser.boxes.apple.AppleTVShowBox;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Change metadata and make sure chunkoffsets are corrected.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class MetaDataWriter {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private FileChannel splitFileAndInsert(File f, long pos, long length) throws IOException {
 | 
				
			||||||
 | 
					        FileChannel read = new RandomAccessFile(f, "r").getChannel();
 | 
				
			||||||
 | 
					        File tmp = File.createTempFile("ChangeMetaData", "splitFileAndInsert");
 | 
				
			||||||
 | 
					        FileChannel tmpWrite = new RandomAccessFile(tmp, "rw").getChannel();
 | 
				
			||||||
 | 
					        read.position(pos);
 | 
				
			||||||
 | 
					        tmpWrite.transferFrom(read, 0, read.size() - pos);
 | 
				
			||||||
 | 
					        read.close();
 | 
				
			||||||
 | 
					        FileChannel write = new RandomAccessFile(f, "rw").getChannel();
 | 
				
			||||||
 | 
					        write.position(pos + length);
 | 
				
			||||||
 | 
					        tmpWrite.position(0);
 | 
				
			||||||
 | 
					        long transferred = 0;
 | 
				
			||||||
 | 
					        while ((transferred += tmpWrite.transferTo(0, tmpWrite.size() - transferred, write)) != tmpWrite.size()) {
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        tmpWrite.close();
 | 
				
			||||||
 | 
					        tmp.delete();
 | 
				
			||||||
 | 
					        return write;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private boolean needsOffsetCorrection(IsoFile isoFile) {
 | 
				
			||||||
 | 
					        if (Path.getPath(isoFile, "moov[0]/mvex[0]") != null) {
 | 
				
			||||||
 | 
					            // Fragmented files don't need a correction
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            // no correction needed if mdat is before moov as insert into moov want change the offsets of mdat
 | 
				
			||||||
 | 
					            for (Box box : isoFile.getBoxes()) {
 | 
				
			||||||
 | 
					                if ("moov".equals(box.getType())) {
 | 
				
			||||||
 | 
					                    return true;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if ("mdat".equals(box.getType())) {
 | 
				
			||||||
 | 
					                    return false;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            throw new RuntimeException("I need moov or mdat. Otherwise all this doesn't make sense");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeMetadata(String videoFilePath, String title, String artist, String show, String episode) throws IOException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        File videoFile = new File(videoFilePath);
 | 
				
			||||||
 | 
					        if (!videoFile.exists()) {
 | 
				
			||||||
 | 
					            throw new FileNotFoundException("File " + videoFilePath + " not exists");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!videoFile.canWrite()) {
 | 
				
			||||||
 | 
					            throw new IllegalStateException("No write permissions to file " + videoFilePath);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        IsoFile isoFile = new IsoFile(videoFilePath);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        MovieBox moov = isoFile.getBoxes(MovieBox.class).get(0);
 | 
				
			||||||
 | 
					        FreeBox freeBox = findFreeBox(moov);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        boolean correctOffset = needsOffsetCorrection(isoFile);
 | 
				
			||||||
 | 
					        long sizeBefore = moov.getSize();
 | 
				
			||||||
 | 
					        long offset = 0;
 | 
				
			||||||
 | 
					        for (Box box : isoFile.getBoxes()) {
 | 
				
			||||||
 | 
					            if ("moov".equals(box.getType())) {
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            offset += box.getSize();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Create structure or just navigate to Apple List Box.
 | 
				
			||||||
 | 
					        UserDataBox userDataBox;
 | 
				
			||||||
 | 
					        if ((userDataBox = Path.getPath(moov, "udta")) == null) {
 | 
				
			||||||
 | 
					            userDataBox = new UserDataBox();
 | 
				
			||||||
 | 
					            moov.addBox(userDataBox);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        MetaBox metaBox;
 | 
				
			||||||
 | 
					        if ((metaBox = Path.getPath(userDataBox, "meta")) == null) {
 | 
				
			||||||
 | 
					            metaBox = new MetaBox();
 | 
				
			||||||
 | 
					            HandlerBox hdlr = new HandlerBox();
 | 
				
			||||||
 | 
					            hdlr.setHandlerType("mdir");
 | 
				
			||||||
 | 
					            metaBox.addBox(hdlr);
 | 
				
			||||||
 | 
					            userDataBox.addBox(metaBox);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        AppleItemListBox ilst;
 | 
				
			||||||
 | 
					        if ((ilst = Path.getPath(metaBox, "ilst")) == null) {
 | 
				
			||||||
 | 
					            ilst = new AppleItemListBox();
 | 
				
			||||||
 | 
					            metaBox.addBox(ilst);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (freeBox == null) {
 | 
				
			||||||
 | 
					            freeBox = new FreeBox(128 * 1024);
 | 
				
			||||||
 | 
					            metaBox.addBox(freeBox);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if(title != null){
 | 
				
			||||||
 | 
					            // Write the title
 | 
				
			||||||
 | 
					            AppleNameBox nam;
 | 
				
			||||||
 | 
					            if ((nam = Path.getPath(ilst, "©nam")) == null) {
 | 
				
			||||||
 | 
					                nam = new AppleNameBox();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            nam.setDataCountry(0);
 | 
				
			||||||
 | 
					            nam.setDataLanguage(0);
 | 
				
			||||||
 | 
					            nam.setValue(title);
 | 
				
			||||||
 | 
					            ilst.addBox(nam);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(artist!=null){
 | 
				
			||||||
 | 
					            // Write the artist
 | 
				
			||||||
 | 
					            AppleArtistBox art;
 | 
				
			||||||
 | 
					            if ((art = Path.getPath(ilst, "©ART")) == null) {
 | 
				
			||||||
 | 
					                art = new AppleArtistBox();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            art.setDataCountry(0);
 | 
				
			||||||
 | 
					            art.setDataLanguage(0);
 | 
				
			||||||
 | 
					            art.setValue(artist);
 | 
				
			||||||
 | 
					            ilst.addBox(art);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(episode!=null){
 | 
				
			||||||
 | 
					            // Write the episode title
 | 
				
			||||||
 | 
					            AppleTVEpisodeNumberBox ep;
 | 
				
			||||||
 | 
					            if ((ep = Path.getPath(ilst, "tven")) == null) {
 | 
				
			||||||
 | 
					                ep = new AppleTVEpisodeNumberBox();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            ep.setDataCountry(0);
 | 
				
			||||||
 | 
					            ep.setDataLanguage(0);
 | 
				
			||||||
 | 
					            ep.setValue(episode);
 | 
				
			||||||
 | 
					            ilst.addBox(ep);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(show!=null){
 | 
				
			||||||
 | 
					            //Write the show
 | 
				
			||||||
 | 
					            AppleTVShowBox sh;
 | 
				
			||||||
 | 
					            if ((sh = Path.getPath(ilst, "tvsh")) == null) {
 | 
				
			||||||
 | 
					                sh = new AppleTVShowBox();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            sh.setDataCountry(0);
 | 
				
			||||||
 | 
					            sh.setDataLanguage(0);
 | 
				
			||||||
 | 
					            sh.setValue(show);
 | 
				
			||||||
 | 
					            ilst.addBox(sh); 
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        long sizeAfter = moov.getSize();
 | 
				
			||||||
 | 
					        long diff = sizeAfter - sizeBefore;
 | 
				
			||||||
 | 
					        // This is the difference of before/after
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // can we compensate by resizing a Free Box we have found?
 | 
				
			||||||
 | 
					        if (freeBox.getData().limit() > diff) {
 | 
				
			||||||
 | 
					            // either shrink or grow!
 | 
				
			||||||
 | 
					            freeBox.setData(ByteBuffer.allocate((int) (freeBox.getData().limit() - diff)));
 | 
				
			||||||
 | 
					            sizeAfter = moov.getSize();
 | 
				
			||||||
 | 
					            diff = sizeAfter - sizeBefore;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (correctOffset && diff != 0) {
 | 
				
			||||||
 | 
					            correctChunkOffsets(moov, diff);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        BetterByteArrayOutputStream baos = new BetterByteArrayOutputStream();
 | 
				
			||||||
 | 
					        moov.getBox(Channels.newChannel(baos));
 | 
				
			||||||
 | 
					        isoFile.close();
 | 
				
			||||||
 | 
					        FileChannel fc;
 | 
				
			||||||
 | 
					        if (diff != 0) {
 | 
				
			||||||
 | 
					            // this is not good: We have to insert bytes in the middle of the file
 | 
				
			||||||
 | 
					            // and this costs time as it requires re-writing most of the file's data
 | 
				
			||||||
 | 
					            fc = splitFileAndInsert(videoFile, offset, sizeAfter - sizeBefore);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            // simple overwrite of something with the file
 | 
				
			||||||
 | 
					            fc = new RandomAccessFile(videoFile, "rw").getChannel();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        fc.position(offset);
 | 
				
			||||||
 | 
					        fc.write(ByteBuffer.wrap(baos.getBuffer(), 0, baos.size()));
 | 
				
			||||||
 | 
					        fc.close();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    FreeBox findFreeBox(Container c) {
 | 
				
			||||||
 | 
					        for (Box box : c.getBoxes()) {
 | 
				
			||||||
 | 
					            if (box instanceof FreeBox) {
 | 
				
			||||||
 | 
					                return (FreeBox) box;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (box instanceof Container) {
 | 
				
			||||||
 | 
					                FreeBox freeBox = findFreeBox((Container) box);
 | 
				
			||||||
 | 
					                if (freeBox != null) {
 | 
				
			||||||
 | 
					                    return freeBox;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void correctChunkOffsets(MovieBox movieBox, long correction) {
 | 
				
			||||||
 | 
					        List<ChunkOffsetBox> chunkOffsetBoxes = Path.getPaths((Box) movieBox, "trak/mdia[0]/minf[0]/stbl[0]/stco[0]");
 | 
				
			||||||
 | 
					        if (chunkOffsetBoxes.isEmpty()) {
 | 
				
			||||||
 | 
					            chunkOffsetBoxes = Path.getPaths((Box) movieBox, "trak/mdia[0]/minf[0]/stbl[0]/st64[0]");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        for (ChunkOffsetBox chunkOffsetBox : chunkOffsetBoxes) {
 | 
				
			||||||
 | 
					            long[] cOffsets = chunkOffsetBox.getChunkOffsets();
 | 
				
			||||||
 | 
					            for (int i = 0; i < cOffsets.length; i++) {
 | 
				
			||||||
 | 
					                cOffsets[i] += correction;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static class BetterByteArrayOutputStream extends ByteArrayOutputStream {
 | 
				
			||||||
 | 
					        byte[] getBuffer() {
 | 
				
			||||||
 | 
					            return buf;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										77
									
								
								src/main/java/com/greinet/tvtotalripper/SwingInterface.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								src/main/java/com/greinet/tvtotalripper/SwingInterface.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,77 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * To change this license header, choose License Headers in Project Properties.
 | 
				
			||||||
 | 
					 * To change this template file, choose Tools | Templates
 | 
				
			||||||
 | 
					 * and open the template in the editor.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					package com.greinet.tvtotalripper;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.awt.Dimension;
 | 
				
			||||||
 | 
					import java.beans.PropertyChangeEvent;
 | 
				
			||||||
 | 
					import java.beans.PropertyChangeListener;
 | 
				
			||||||
 | 
					import java.util.ArrayList;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					import javax.swing.JFrame;
 | 
				
			||||||
 | 
					import javax.swing.JLabel;
 | 
				
			||||||
 | 
					import javax.swing.JProgressBar;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author agreiner
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class SwingInterface implements PropertyChangeListener {
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private final JFrame frame;
 | 
				
			||||||
 | 
					    private final JLabel label;
 | 
				
			||||||
 | 
					    private final JProgressBar progressBar;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private List<PropertyChangeListener> changeListener = new ArrayList<>();
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public SwingInterface(){
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        frame = new JFrame("TV Total Ripper");
 | 
				
			||||||
 | 
					        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        label = new JLabel("Testlabel");
 | 
				
			||||||
 | 
					        label.setPreferredSize(new Dimension(200, 30));
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        progressBar = new JProgressBar(0, 100);
 | 
				
			||||||
 | 
					        progressBar.setPreferredSize(new Dimension(200, 30));
 | 
				
			||||||
 | 
					        progressBar.setStringPainted(true);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        frame.add(label);
 | 
				
			||||||
 | 
					        frame.add(progressBar);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        frame.pack();
 | 
				
			||||||
 | 
					        frame.setVisible(true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public static void main(String[] args) {
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        javax.swing.SwingUtilities.invokeLater(new Runnable() {
 | 
				
			||||||
 | 
					            @Override
 | 
				
			||||||
 | 
					            public void run() {
 | 
				
			||||||
 | 
					                SwingInterface i = new SwingInterface();
 | 
				
			||||||
 | 
					                i.startDownload();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void startDownload(){
 | 
				
			||||||
 | 
					        //DownloadTask task = new DownloadTask(label, "https://cldf-od.r53.cdn.tv1.eu/secdl/06d6d246daa2c7ec0ffb2f8281149072/6066001f/11021brainpool/ondemand/3583brainpool/163840/myspass2009/11/33/2171/9642/9642_61.mp4", "");
 | 
				
			||||||
 | 
					        //task.addPropertyChangeListener(this);
 | 
				
			||||||
 | 
					        //task.execute();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void propertyChange(PropertyChangeEvent evt) {
 | 
				
			||||||
 | 
					         if (evt.getPropertyName().equals("progress1")) {
 | 
				
			||||||
 | 
					            int progress = (Integer) evt.getNewValue();
 | 
				
			||||||
 | 
					            progressBar.setValue(progress);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										179
									
								
								src/main/java/com/greinet/tvtotalripper/crawler/CrawlerUtil.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										179
									
								
								src/main/java/com/greinet/tvtotalripper/crawler/CrawlerUtil.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,179 @@
 | 
				
			|||||||
 | 
					package com.greinet.tvtotalripper.crawler;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.ArrayList;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					import java.util.Optional;
 | 
				
			||||||
 | 
					import java.util.stream.Collectors;
 | 
				
			||||||
 | 
					import org.apache.logging.log4j.Level;
 | 
				
			||||||
 | 
					import org.apache.logging.log4j.LogManager;
 | 
				
			||||||
 | 
					import org.apache.logging.log4j.Logger;
 | 
				
			||||||
 | 
					import org.openqa.selenium.By;
 | 
				
			||||||
 | 
					import org.openqa.selenium.WebDriver;
 | 
				
			||||||
 | 
					import org.openqa.selenium.WebElement;
 | 
				
			||||||
 | 
					import org.openqa.selenium.chrome.ChromeDriver;
 | 
				
			||||||
 | 
					import org.openqa.selenium.chrome.ChromeOptions;
 | 
				
			||||||
 | 
					import org.openqa.selenium.support.ui.ExpectedConditions;
 | 
				
			||||||
 | 
					import org.openqa.selenium.support.ui.WebDriverWait;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Helper class to get show, season und episode elements from the website
 | 
				
			||||||
 | 
					 * @author agreiner
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class CrawlerUtil {
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    static{
 | 
				
			||||||
 | 
					        System.setProperty("webdriver.chrome.driver", "resources/chromedriver.exe");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /** The logger */
 | 
				
			||||||
 | 
					    private static final Logger logger = LogManager.getLogger(CrawlerUtil.class);
 | 
				
			||||||
 | 
					    /** The WebDriver for show information */
 | 
				
			||||||
 | 
					    private final static WebDriver driverShows = createShowDriver();
 | 
				
			||||||
 | 
					    /** The WebDriver for season and episode information */
 | 
				
			||||||
 | 
					    private final static WebDriver driverSeasonsAndEpisodes = createChromeDriver();
 | 
				
			||||||
 | 
					    /** The WebDriver for fetchfile.net URL conversion */
 | 
				
			||||||
 | 
					    private final static WebDriver driverFetchFile = createFetchFileDriver();
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public static String show = "";
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Create a basic ChromeDriver
 | 
				
			||||||
 | 
					     * @return 
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private static WebDriver createChromeDriver(){
 | 
				
			||||||
 | 
					        ChromeOptions options = new ChromeOptions();
 | 
				
			||||||
 | 
					        options.addArguments("start-maximized");
 | 
				
			||||||
 | 
					        return new ChromeDriver(options);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Create the WebDriver to get show information
 | 
				
			||||||
 | 
					     * @return the WebDriver
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private static WebDriver createShowDriver(){
 | 
				
			||||||
 | 
					        WebDriver driver = createChromeDriver();
 | 
				
			||||||
 | 
					        driver.get("https://www.myspass.de/sendungen-a-bis-z/");
 | 
				
			||||||
 | 
					        return driver;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private static WebDriver createFetchFileDriver(){
 | 
				
			||||||
 | 
					        WebDriver driver = createChromeDriver();
 | 
				
			||||||
 | 
					        driver.get("https://de.fetchfile.net/herunterladen-von-myspass");
 | 
				
			||||||
 | 
					        return driver;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Navigate to the given URL or do nothing, if already at that URL
 | 
				
			||||||
 | 
					     * @param url the URL
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private static void navigateToShow(String url){
 | 
				
			||||||
 | 
					        if(!driverSeasonsAndEpisodes.getCurrentUrl().equals(url)){
 | 
				
			||||||
 | 
					            driverSeasonsAndEpisodes.get(url);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        driverSeasonsAndEpisodes.findElement(By.xpath("/html/body/div[4]/div[1]/div[2]/ul/li[2]/a")).click();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the WebElements representing the shows.
 | 
				
			||||||
 | 
					     * @return the show WebElements
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static List<WebElement> getShows(){
 | 
				
			||||||
 | 
					        return driverShows.findElements(By.xpath("/html/body/div[5]/div/div/div/div/a"));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the Webelements represeting the seasons of a specific show
 | 
				
			||||||
 | 
					     * @param url the URL to the show
 | 
				
			||||||
 | 
					     * @return the season WebElements
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static List<WebElement> getSeasons(String url){
 | 
				
			||||||
 | 
					        navigateToShow(url);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        String urlNotTrailingSlash = url.substring(0,url.lastIndexOf("/"));
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        show = urlNotTrailingSlash.substring(urlNotTrailingSlash.lastIndexOf("/")+1, urlNotTrailingSlash.length());
 | 
				
			||||||
 | 
					        return driverSeasonsAndEpisodes.findElements(By.xpath("/html/body/div[4]/div[1]/div[3]/div[2]/div[1]/select/option"));
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the information from the episodes from a specific show and season packed into a wrapper class
 | 
				
			||||||
 | 
					     * @param url the URL of the show
 | 
				
			||||||
 | 
					     * @param seasonName the name of the season
 | 
				
			||||||
 | 
					     * @return the list of episodes
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static List<EpisodeWrapper> getEpisodes(String url, String seasonName){
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // navigate the WebDriver to the specified URL
 | 
				
			||||||
 | 
					        navigateToShow(url);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Search the specified season
 | 
				
			||||||
 | 
					        List<WebElement> elements = driverSeasonsAndEpisodes.findElements(By.xpath("/html/body/div[4]/div[1]/div[3]/div[2]/div[1]/select/option"));
 | 
				
			||||||
 | 
					        Optional<WebElement> seasonElement = elements.stream().filter(e -> e.getText().equals(seasonName)).findFirst();
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // If the season was found, navigate there
 | 
				
			||||||
 | 
					        if(seasonElement.isPresent()){
 | 
				
			||||||
 | 
					            seasonElement.get().click();
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // Wait for the website to load and then switch to the list view
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                Thread.sleep(2000);
 | 
				
			||||||
 | 
					            } catch (InterruptedException ex) {
 | 
				
			||||||
 | 
					                logger.log(Level.ERROR, "Could not find list view button for url:[{0}], season:[{1}]",url, seasonName);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            driverSeasonsAndEpisodes.findElement(By.className("listView--toggle_input")).click();
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // Get the parent div of the episode information list
 | 
				
			||||||
 | 
					            List<WebElement> episodeListDivs = driverSeasonsAndEpisodes.findElements(By.xpath("/html/body/div[4]/div[1]/div[3]/div[2]/div"));
 | 
				
			||||||
 | 
					            List<WebElement> filteredNoDisplay = episodeListDivs.stream().filter(e -> !"none".equals(e.getCssValue("display"))).collect(Collectors.toList());
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if(!filteredNoDisplay.isEmpty()){
 | 
				
			||||||
 | 
					                // Get the Webelements with the link and title information
 | 
				
			||||||
 | 
					                List<WebElement> linksToEpisodes = new ArrayList<>();
 | 
				
			||||||
 | 
					                filteredNoDisplay.forEach(e -> 
 | 
				
			||||||
 | 
					                    linksToEpisodes.addAll(e.findElements(By.xpath(".//div/table/tbody/tr/td/a")))
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					                // Get the WebElements with the duration information
 | 
				
			||||||
 | 
					                List<WebElement> durationOfEpisodes = new ArrayList<>();
 | 
				
			||||||
 | 
					                filteredNoDisplay.forEach(e -> 
 | 
				
			||||||
 | 
					                    durationOfEpisodes.addAll(e.findElements(By.xpath(".//div/table/tbody/tr/td[3]")))
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					                // Put the information into episode wrappers and return the list
 | 
				
			||||||
 | 
					                List<EpisodeWrapper> wrappers = new ArrayList<>();
 | 
				
			||||||
 | 
					                for(int i=0;i<linksToEpisodes.size();i++){
 | 
				
			||||||
 | 
					                    wrappers.add(new EpisodeWrapper(linksToEpisodes.get(i).getAttribute("href"), linksToEpisodes.get(i).getText(), linksToEpisodes.get(i).getText(), seasonName, show, durationOfEpisodes.get(i).getText()));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                return wrappers;
 | 
				
			||||||
 | 
					            }else{
 | 
				
			||||||
 | 
					                logger.log(Level.WARN, "No episodes found for url:[{0}], season:[{1}]",url, seasonName);
 | 
				
			||||||
 | 
					                return new ArrayList<>();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }else{
 | 
				
			||||||
 | 
					            logger.log(Level.ERROR, "No seaons found for url:[{0}], season:[{1}]",url, seasonName);
 | 
				
			||||||
 | 
					            return new ArrayList<>();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public static String getFetchfileURL(String myspassURL){
 | 
				
			||||||
 | 
					        synchronized(driverFetchFile){
 | 
				
			||||||
 | 
					            WebElement videoPathInput = driverFetchFile.findElement(By.id("videoPath"));
 | 
				
			||||||
 | 
					            videoPathInput.sendKeys(myspassURL);
 | 
				
			||||||
 | 
					            WebElement submitButton = driverFetchFile.findElement(By.id("home-submit"));
 | 
				
			||||||
 | 
					            submitButton.click();
 | 
				
			||||||
 | 
					            WebDriverWait wait = new WebDriverWait(driverFetchFile, 20);
 | 
				
			||||||
 | 
					            WebElement downloadButton = wait.until(ExpectedConditions.elementToBeClickable(By.className("download-link")));
 | 
				
			||||||
 | 
					            String downloadLink = downloadButton.getAttribute("href");
 | 
				
			||||||
 | 
					            driverFetchFile.get("https://de.fetchfile.net/herunterladen-von-myspass");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Close advertisement tabs
 | 
				
			||||||
 | 
					            List<String> tabs = new ArrayList<>(driverFetchFile.getWindowHandles());
 | 
				
			||||||
 | 
					            for (int i=1;i<tabs.size();i++){
 | 
				
			||||||
 | 
					                driverFetchFile.switchTo().window(tabs.get(i));
 | 
				
			||||||
 | 
					                driverFetchFile.close();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            driverFetchFile.switchTo().window(driverFetchFile.getWindowHandles().toArray(new String[1])[0]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return downloadLink;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,168 @@
 | 
				
			|||||||
 | 
					package com.greinet.tvtotalripper.crawler;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.Objects;
 | 
				
			||||||
 | 
					import java.util.UUID;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * A wrapper class to collect all episode information
 | 
				
			||||||
 | 
					 * @author agreiner
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class EpisodeWrapper {
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /** The URL to the episode */
 | 
				
			||||||
 | 
					    private String url;
 | 
				
			||||||
 | 
					    /** The title */
 | 
				
			||||||
 | 
					    private String title;
 | 
				
			||||||
 | 
					    /** The episode */
 | 
				
			||||||
 | 
					    private String episode;
 | 
				
			||||||
 | 
					    /** The season */
 | 
				
			||||||
 | 
					    private String season;
 | 
				
			||||||
 | 
					    /** The episode duration */
 | 
				
			||||||
 | 
					    private String duration;
 | 
				
			||||||
 | 
					    /** The unique id */
 | 
				
			||||||
 | 
					    private final long id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private String show;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Create a episode wrapper to store episode information
 | 
				
			||||||
 | 
					     * @param url the URL
 | 
				
			||||||
 | 
					     * @param title the title
 | 
				
			||||||
 | 
					     * @param episodeNumber the episode
 | 
				
			||||||
 | 
					     * @param season the season
 | 
				
			||||||
 | 
					     * @param show the show
 | 
				
			||||||
 | 
					     * @param duration the duration
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public EpisodeWrapper(String url, String title, String episode, String season, String show, String duration) {
 | 
				
			||||||
 | 
					        this.url = url;
 | 
				
			||||||
 | 
					        this.title = title;
 | 
				
			||||||
 | 
					        this.episode = episode;
 | 
				
			||||||
 | 
					        this.season = season;
 | 
				
			||||||
 | 
					        this.show = show;
 | 
				
			||||||
 | 
					        this.duration = duration;
 | 
				
			||||||
 | 
					        id = UUID.randomUUID().getLeastSignificantBits();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String getShow() {
 | 
				
			||||||
 | 
					        return show;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setShow(String show) {
 | 
				
			||||||
 | 
					        this.show = show;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Return the unique id
 | 
				
			||||||
 | 
					     * @return the id
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public long getId() {
 | 
				
			||||||
 | 
					        return id;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get the URL to the episode
 | 
				
			||||||
 | 
					     * @return the URL
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public String getUrl() {
 | 
				
			||||||
 | 
					        return url;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Set the URL to the episode
 | 
				
			||||||
 | 
					     * @param url the URL
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setUrl(String url) {
 | 
				
			||||||
 | 
					        this.url = url;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get the title of the episode
 | 
				
			||||||
 | 
					     * @return the title
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public String getTitle() {
 | 
				
			||||||
 | 
					        return title;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Set the title of the episode 
 | 
				
			||||||
 | 
					     * @param title the title
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setTitle(String title) {
 | 
				
			||||||
 | 
					        this.title = title;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get the episode 
 | 
				
			||||||
 | 
					     * @return the episode 
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public String getEpisode() {
 | 
				
			||||||
 | 
					        return episode;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Set the episode number
 | 
				
			||||||
 | 
					     * @param episode the episode
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setEpisodeNumber(String episode) {
 | 
				
			||||||
 | 
					        this.episode = episode;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get the season of the episode
 | 
				
			||||||
 | 
					     * @return the season
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public String getSeason() {
 | 
				
			||||||
 | 
					        return season;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Set the season of the episode
 | 
				
			||||||
 | 
					     * @param season the season
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setSeason(String season) {
 | 
				
			||||||
 | 
					        this.season = season;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get the duration of the episode
 | 
				
			||||||
 | 
					     * @return the duration
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public String getDuration() {
 | 
				
			||||||
 | 
					        return duration;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Set the duration of the episode
 | 
				
			||||||
 | 
					     * @param duration the duration
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setDuration(String duration) {
 | 
				
			||||||
 | 
					        this.duration = duration;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public String toString() {
 | 
				
			||||||
 | 
					        return title;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public int hashCode() {
 | 
				
			||||||
 | 
					        int hash = 3;
 | 
				
			||||||
 | 
					        hash = 19 * hash + Objects.hashCode(this.url);
 | 
				
			||||||
 | 
					        return hash;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public boolean equals(Object obj) {
 | 
				
			||||||
 | 
					        if (this == obj) {
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (obj == null) {
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (getClass() != obj.getClass()) {
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        final EpisodeWrapper other = (EpisodeWrapper) obj;
 | 
				
			||||||
 | 
					        return Objects.equals(this.url, other.url);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,81 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * To change this license header, choose License Headers in Project Properties.
 | 
				
			||||||
 | 
					 * To change this template file, choose Tools | Templates
 | 
				
			||||||
 | 
					 * and open the template in the editor.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					package com.greinet.tvtotalripper.download;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.io.InputStream;
 | 
				
			||||||
 | 
					import java.net.HttpURLConnection;
 | 
				
			||||||
 | 
					import java.net.URL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author agreiner
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class ConnectionUtil {
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private HttpURLConnection httpConn;
 | 
				
			||||||
 | 
					    private InputStream inputStream;
 | 
				
			||||||
 | 
					    private String fileName;
 | 
				
			||||||
 | 
					    private int contentLength;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public void prepare(String fileUrl) throws IOException{
 | 
				
			||||||
 | 
					        URL url = new URL(fileUrl);
 | 
				
			||||||
 | 
					        httpConn = (HttpURLConnection) url.openConnection();
 | 
				
			||||||
 | 
					        int responseCode = httpConn.getResponseCode();
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // always check HTTP response code first
 | 
				
			||||||
 | 
					        if (responseCode == HttpURLConnection.HTTP_OK) {
 | 
				
			||||||
 | 
					            String disposition = httpConn.getHeaderField("Content-Disposition");
 | 
				
			||||||
 | 
					            String contentType = httpConn.getContentType();
 | 
				
			||||||
 | 
					            contentLength = httpConn.getContentLength();
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					            if (disposition != null) {
 | 
				
			||||||
 | 
					                // extracts file name from header field
 | 
				
			||||||
 | 
					                int index = disposition.indexOf("filename=");
 | 
				
			||||||
 | 
					                if (index > 0) {
 | 
				
			||||||
 | 
					                    fileName = disposition.substring(index + 10,
 | 
				
			||||||
 | 
					                            disposition.length() - 1);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                // extracts file name from URL
 | 
				
			||||||
 | 
					                fileName = fileUrl.substring(fileUrl.lastIndexOf("/") + 1,
 | 
				
			||||||
 | 
					                        fileUrl.length());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					            // output for debugging purpose only
 | 
				
			||||||
 | 
					            System.out.println("Content-Type = " + contentType);
 | 
				
			||||||
 | 
					            System.out.println("Content-Disposition = " + disposition);
 | 
				
			||||||
 | 
					            System.out.println("Content-Length = " + contentLength);
 | 
				
			||||||
 | 
					            System.out.println("fileName = " + fileName);
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					            // opens input stream from the HTTP connection
 | 
				
			||||||
 | 
					            inputStream = httpConn.getInputStream();
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            throw new IOException(
 | 
				
			||||||
 | 
					                    "No file to download. Server replied HTTP code: "
 | 
				
			||||||
 | 
					                            + responseCode);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public void disconnect() throws IOException {
 | 
				
			||||||
 | 
					        inputStream.close();
 | 
				
			||||||
 | 
					        httpConn.disconnect();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					    public String getFileName() {
 | 
				
			||||||
 | 
					        return this.fileName;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					    public int getContentLength() {
 | 
				
			||||||
 | 
					        return this.contentLength;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					    public InputStream getInputStream() {
 | 
				
			||||||
 | 
					        return this.inputStream;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,95 @@
 | 
				
			|||||||
 | 
					package com.greinet.tvtotalripper.download;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.greinet.tvtotalripper.MetaDataWriter;
 | 
				
			||||||
 | 
					import com.greinet.tvtotalripper.crawler.CrawlerUtil;
 | 
				
			||||||
 | 
					import com.greinet.tvtotalripper.crawler.EpisodeWrapper;
 | 
				
			||||||
 | 
					import com.greinet.tvtotalripper.ui.SettingsRipperPanel;
 | 
				
			||||||
 | 
					import java.io.File;
 | 
				
			||||||
 | 
					import java.io.FileOutputStream;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.io.InputStream;
 | 
				
			||||||
 | 
					import javax.swing.SwingWorker;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author agreiner
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class DownloadTask extends SwingWorker<Void, Void> {
 | 
				
			||||||
 | 
					    private static final int BUFFER_SIZE = 4096;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private final EpisodeWrapper episodeWrapper;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					     
 | 
				
			||||||
 | 
					    public DownloadTask(EpisodeWrapper episodeWrapper) {
 | 
				
			||||||
 | 
					        this.episodeWrapper = episodeWrapper;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public EpisodeWrapper getEpisodeWrapper() {
 | 
				
			||||||
 | 
					        return episodeWrapper;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public String toString() {
 | 
				
			||||||
 | 
					        return episodeWrapper.getTitle();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public long fileSize = 0;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public long totalBytesRead = 0;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public int percentCompleted = 0;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Executed in background thread
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    protected Void doInBackground() throws Exception {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            String downloadURL = CrawlerUtil.getFetchfileURL(episodeWrapper.getUrl());
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            ConnectionUtil util = new ConnectionUtil();
 | 
				
			||||||
 | 
					            util.prepare(downloadURL);
 | 
				
			||||||
 | 
					             
 | 
				
			||||||
 | 
					            InputStream inputStream = util.getInputStream();
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            String fixedTitle = episodeWrapper.getTitle().replace(":", " ");
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            File outputFile = new File(SettingsRipperPanel.DOWNLOADFOLDER, fixedTitle+".mp4");
 | 
				
			||||||
 | 
					            FileOutputStream outputStream = new FileOutputStream(outputFile);
 | 
				
			||||||
 | 
					            byte[] buffer = new byte[BUFFER_SIZE];
 | 
				
			||||||
 | 
					            int bytesRead = -1;
 | 
				
			||||||
 | 
					            totalBytesRead = 0;
 | 
				
			||||||
 | 
					            percentCompleted = 0;
 | 
				
			||||||
 | 
					            int oldPercentCompleted = 0;
 | 
				
			||||||
 | 
					            fileSize = util.getContentLength();
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					            while ((bytesRead = inputStream.read(buffer)) != -1) {
 | 
				
			||||||
 | 
					                outputStream.write(buffer, 0, bytesRead);
 | 
				
			||||||
 | 
					                totalBytesRead += bytesRead;
 | 
				
			||||||
 | 
					                percentCompleted = (int) (totalBytesRead * 100 / fileSize);
 | 
				
			||||||
 | 
					                firePropertyChange(Long.toString(episodeWrapper.getId()), oldPercentCompleted, percentCompleted);
 | 
				
			||||||
 | 
					                oldPercentCompleted = percentCompleted;
 | 
				
			||||||
 | 
					                //setProgress(percentCompleted);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					            outputStream.close();
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					            util.disconnect();
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            MetaDataWriter mdp = new MetaDataWriter();
 | 
				
			||||||
 | 
					            mdp.writeMetadata(outputFile.getAbsolutePath(), episodeWrapper.getTitle(), episodeWrapper.getShow(), episodeWrapper.getShow(), episodeWrapper.getEpisode());
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        } catch (IOException ex) {
 | 
				
			||||||
 | 
					            cancel(true);          
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Executed in Swing's event dispatching thread
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    protected void done() {
 | 
				
			||||||
 | 
					        firePropertyChange(Long.toString(episodeWrapper.getId()), 0, 101);
 | 
				
			||||||
 | 
					    }  
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										116
									
								
								src/main/java/com/greinet/tvtotalripper/ui/BaseRipperPanel.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								src/main/java/com/greinet/tvtotalripper/ui/BaseRipperPanel.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,116 @@
 | 
				
			|||||||
 | 
					package com.greinet.tvtotalripper.ui;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.awt.Color;
 | 
				
			||||||
 | 
					import java.awt.GridBagConstraints;
 | 
				
			||||||
 | 
					import java.awt.GridBagLayout;
 | 
				
			||||||
 | 
					import java.util.ArrayList;
 | 
				
			||||||
 | 
					import java.util.Collections;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					import javax.swing.BorderFactory;
 | 
				
			||||||
 | 
					import javax.swing.JButton;
 | 
				
			||||||
 | 
					import javax.swing.JLabel;
 | 
				
			||||||
 | 
					import javax.swing.JList;
 | 
				
			||||||
 | 
					import javax.swing.JPanel;
 | 
				
			||||||
 | 
					import javax.swing.JScrollPane;
 | 
				
			||||||
 | 
					import javax.swing.JTextField;
 | 
				
			||||||
 | 
					import javax.swing.event.ListSelectionEvent;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author agreiner
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class BaseRipperPanel {
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private final JPanel panel;
 | 
				
			||||||
 | 
					    private JList<String> listPanel;
 | 
				
			||||||
 | 
					    private JTextField textField;
 | 
				
			||||||
 | 
					    private final JButton button;
 | 
				
			||||||
 | 
					    private final JLabel label;
 | 
				
			||||||
 | 
					    private Map<String, String> elements;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public BaseRipperPanel(Map<String, String> elements){
 | 
				
			||||||
 | 
					        this.elements = elements;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if(elements.isEmpty()){
 | 
				
			||||||
 | 
					            elements.put("Dummy-Key", "Dummy-URL");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        panel = new JPanel(false);
 | 
				
			||||||
 | 
					        panel.setLayout(new GridBagLayout());
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        GridBagConstraints c = new GridBagConstraints();
 | 
				
			||||||
 | 
					        c.gridx = 0;
 | 
				
			||||||
 | 
					        c.gridy = 0;
 | 
				
			||||||
 | 
					        c.gridwidth = 3;
 | 
				
			||||||
 | 
					        c.fill = GridBagConstraints.BOTH;
 | 
				
			||||||
 | 
					        c.weightx = 0.75;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        textField = new JTextField();
 | 
				
			||||||
 | 
					        textField.setEditable(false);
 | 
				
			||||||
 | 
					        panel.add(textField,c);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        button = new JButton("Select");
 | 
				
			||||||
 | 
					        c.gridx = 3;
 | 
				
			||||||
 | 
					        c.gridwidth = 2;
 | 
				
			||||||
 | 
					        c.weightx = 0.25;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        panel.add(button,c);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        List<String> names = new ArrayList<>(elements.keySet());
 | 
				
			||||||
 | 
					        Collections.sort(names);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        listPanel = new JList<>(names.toArray(new String[elements.keySet().size()]));
 | 
				
			||||||
 | 
					        JScrollPane scrollPane = new JScrollPane();
 | 
				
			||||||
 | 
					        scrollPane.setViewportView(listPanel);
 | 
				
			||||||
 | 
					        listPanel.setLayoutOrientation(JList.VERTICAL);
 | 
				
			||||||
 | 
					        listPanel.setSelectedIndex(0);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        listPanel.addListSelectionListener((ListSelectionEvent e) -> {
 | 
				
			||||||
 | 
					            textField.setText(listPanel.getSelectedValue());
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        textField.setText(listPanel.getSelectedValue());
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        c.gridx = 0;
 | 
				
			||||||
 | 
					        c.gridy = 1;
 | 
				
			||||||
 | 
					        c.gridwidth = 5;
 | 
				
			||||||
 | 
					        c.gridheight = 5;
 | 
				
			||||||
 | 
					        c.weightx = 0;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        panel.add(scrollPane,c);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        label = new JLabel(Integer.toString(elements.keySet().size()));
 | 
				
			||||||
 | 
					        label.setBorder(BorderFactory.createLineBorder(Color.BLACK, 1));
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        c.gridx = 4;
 | 
				
			||||||
 | 
					        c.gridy = 6;
 | 
				
			||||||
 | 
					        c.gridwidth = 1;
 | 
				
			||||||
 | 
					        c.gridheight = 1;
 | 
				
			||||||
 | 
					        c.fill = GridBagConstraints.NONE;
 | 
				
			||||||
 | 
					        c.anchor = GridBagConstraints.EAST;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        panel.add(label, c);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public JPanel getJPanel(){
 | 
				
			||||||
 | 
					        return panel;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public JButton getJButton(){
 | 
				
			||||||
 | 
					        return button;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public String getCurrentSelected(){
 | 
				
			||||||
 | 
					        return elements.get(listPanel.getSelectedValue());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public JList getJList(){
 | 
				
			||||||
 | 
					        return listPanel;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,143 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * To change this license header, choose License Headers in Project Properties.
 | 
				
			||||||
 | 
					 * To change this template file, choose Tools | Templates
 | 
				
			||||||
 | 
					 * and open the template in the editor.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					package com.greinet.tvtotalripper.ui;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.greinet.tvtotalripper.download.DownloadTask;
 | 
				
			||||||
 | 
					import java.awt.Color;
 | 
				
			||||||
 | 
					import java.awt.GridBagConstraints;
 | 
				
			||||||
 | 
					import java.awt.GridBagLayout;
 | 
				
			||||||
 | 
					import java.beans.PropertyChangeEvent;
 | 
				
			||||||
 | 
					import java.beans.PropertyChangeListener;
 | 
				
			||||||
 | 
					import java.util.ArrayList;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					import java.util.Optional;
 | 
				
			||||||
 | 
					import javax.swing.BorderFactory;
 | 
				
			||||||
 | 
					import javax.swing.DefaultListModel;
 | 
				
			||||||
 | 
					import javax.swing.JLabel;
 | 
				
			||||||
 | 
					import javax.swing.JList;
 | 
				
			||||||
 | 
					import javax.swing.JPanel;
 | 
				
			||||||
 | 
					import javax.swing.JScrollPane;
 | 
				
			||||||
 | 
					import javax.swing.JTextField;
 | 
				
			||||||
 | 
					import javax.swing.ListSelectionModel;
 | 
				
			||||||
 | 
					import javax.swing.event.ListSelectionEvent;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author agreiner
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class DownloadRipperPanel implements PropertyChangeListener{
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private final JPanel panel;
 | 
				
			||||||
 | 
					    private JList<DownloadTask> listTasks;
 | 
				
			||||||
 | 
					    private DefaultListModel<DownloadTask> listModel;
 | 
				
			||||||
 | 
					    private JTextField textSelectedTask;
 | 
				
			||||||
 | 
					    private final JLabel labelTaskCount;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private final List<DownloadTask> downloadTasks;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private DownloadTaskInformationPanel infoPanel;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public DownloadRipperPanel(){
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        downloadTasks = new ArrayList<>();
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        panel = new JPanel();
 | 
				
			||||||
 | 
					        panel.setLayout(new GridBagLayout());
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        GridBagConstraints c = new GridBagConstraints();
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        c.gridx = 0;
 | 
				
			||||||
 | 
					        c.gridy = 0;
 | 
				
			||||||
 | 
					        c.gridwidth = 5;
 | 
				
			||||||
 | 
					        c.fill = GridBagConstraints.BOTH;
 | 
				
			||||||
 | 
					        c.weightx = 0.5;
 | 
				
			||||||
 | 
					        textSelectedTask = new JTextField();
 | 
				
			||||||
 | 
					        textSelectedTask.setEditable(false);
 | 
				
			||||||
 | 
					        panel.add(textSelectedTask, c);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        listModel = new DefaultListModel<>();
 | 
				
			||||||
 | 
					        listTasks = new JList<>(listModel);
 | 
				
			||||||
 | 
					        listTasks.setSelectedIndex(ListSelectionModel.SINGLE_SELECTION);
 | 
				
			||||||
 | 
					        JScrollPane scrollPane = new JScrollPane();
 | 
				
			||||||
 | 
					        scrollPane.setViewportView(listTasks);
 | 
				
			||||||
 | 
					        listTasks.setLayoutOrientation(JList.VERTICAL);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        c.gridx = 0;
 | 
				
			||||||
 | 
					        c.gridy = 1;
 | 
				
			||||||
 | 
					        c.gridwidth = 5;
 | 
				
			||||||
 | 
					        c.gridheight = 5;
 | 
				
			||||||
 | 
					        c.weightx = 0.5;
 | 
				
			||||||
 | 
					        panel.add(scrollPane, c);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        infoPanel = new DownloadTaskInformationPanel();
 | 
				
			||||||
 | 
					        c.gridx = 5;
 | 
				
			||||||
 | 
					        c.gridy = 0;
 | 
				
			||||||
 | 
					        c.gridwidth = 5;
 | 
				
			||||||
 | 
					        c.weightx = 0.5;
 | 
				
			||||||
 | 
					        c.fill = GridBagConstraints.BOTH;
 | 
				
			||||||
 | 
					        panel.add(infoPanel, c);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        listTasks.addListSelectionListener((ListSelectionEvent e) -> {
 | 
				
			||||||
 | 
					            if(listTasks.getSelectedValue()!=null){
 | 
				
			||||||
 | 
					                textSelectedTask.setText(listTasks.getSelectedValue().toString());
 | 
				
			||||||
 | 
					                infoPanel.setInformation(listTasks.getSelectedValue());
 | 
				
			||||||
 | 
					                infoPanel.selectedDownloadTask = listTasks.getSelectedValue();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        labelTaskCount = new JLabel(Integer.toString(listTasks.getModel().getSize()));
 | 
				
			||||||
 | 
					        labelTaskCount.setBorder(BorderFactory.createLineBorder(Color.BLACK, 1));
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        c.gridx = 4;
 | 
				
			||||||
 | 
					        c.gridy = 6;
 | 
				
			||||||
 | 
					        c.gridwidth = 1;
 | 
				
			||||||
 | 
					        c.gridheight = 1;
 | 
				
			||||||
 | 
					        c.fill = GridBagConstraints.NONE;
 | 
				
			||||||
 | 
					        c.anchor = GridBagConstraints.EAST;
 | 
				
			||||||
 | 
					        panel.add(labelTaskCount, c);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public JPanel getJPanel(){
 | 
				
			||||||
 | 
					        return panel;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public DownloadTask getSelectedTask(){
 | 
				
			||||||
 | 
					        return listTasks.getSelectedValue();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void addTask(DownloadTask task){
 | 
				
			||||||
 | 
					        downloadTasks.add(task);
 | 
				
			||||||
 | 
					        task.execute();
 | 
				
			||||||
 | 
					        listModel.addElement(task);
 | 
				
			||||||
 | 
					        updateTaskCount();
 | 
				
			||||||
 | 
					        task.addPropertyChangeListener(this);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void propertyChange(PropertyChangeEvent evt) {
 | 
				
			||||||
 | 
					        if(evt.getNewValue().equals(101)){
 | 
				
			||||||
 | 
					            Optional<DownloadTask> taskToRemove = downloadTasks.stream().filter(t -> Long.toString(t.getEpisodeWrapper().getId()).equals(evt.getPropertyName())).findFirst();
 | 
				
			||||||
 | 
					            if(taskToRemove.isPresent()){
 | 
				
			||||||
 | 
					                if(listTasks.getSelectedValue() != null && taskToRemove.get().equals(listTasks.getSelectedValue())){
 | 
				
			||||||
 | 
					                    infoPanel.clearInformation();
 | 
				
			||||||
 | 
					                    textSelectedTask.setText("");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                downloadTasks.remove(taskToRemove.get());
 | 
				
			||||||
 | 
					                listModel.removeElement(taskToRemove.get());
 | 
				
			||||||
 | 
					                updateTaskCount();
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        panel.repaint();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private void updateTaskCount(){
 | 
				
			||||||
 | 
					        labelTaskCount.setText(Integer.toString(listTasks.getModel().getSize()));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,126 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * To change this license header, choose License Headers in Project Properties.
 | 
				
			||||||
 | 
					 * To change this template file, choose Tools | Templates
 | 
				
			||||||
 | 
					 * and open the template in the editor.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					package com.greinet.tvtotalripper.ui;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.greinet.tvtotalripper.download.DownloadTask;
 | 
				
			||||||
 | 
					import java.awt.GridLayout;
 | 
				
			||||||
 | 
					import java.beans.PropertyChangeEvent;
 | 
				
			||||||
 | 
					import java.beans.PropertyChangeListener;
 | 
				
			||||||
 | 
					import javax.swing.JLabel;
 | 
				
			||||||
 | 
					import javax.swing.JPanel;
 | 
				
			||||||
 | 
					import javax.swing.JProgressBar;
 | 
				
			||||||
 | 
					import javax.swing.JTextField;
 | 
				
			||||||
 | 
					import org.apache.commons.io.FileUtils;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author agreiner
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class DownloadTaskInformationPanel extends JPanel{
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private JLabel labelTitle;
 | 
				
			||||||
 | 
					    private JLabel labelShow;
 | 
				
			||||||
 | 
					    private JLabel labelSeason;
 | 
				
			||||||
 | 
					    private JLabel labelEpisode;
 | 
				
			||||||
 | 
					    private JLabel labelDuration;
 | 
				
			||||||
 | 
					    private JLabel labelProgress;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private JTextField textTitle;
 | 
				
			||||||
 | 
					    private JTextField textShow;
 | 
				
			||||||
 | 
					    private JTextField textSeason;
 | 
				
			||||||
 | 
					    private JTextField textEpisode;
 | 
				
			||||||
 | 
					    private JTextField textDuration;
 | 
				
			||||||
 | 
					    private JProgressBar progressBar;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public DownloadTask selectedDownloadTask;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public DownloadTaskInformationPanel(){
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        labelTitle = new JLabel("Title");
 | 
				
			||||||
 | 
					        labelShow = new JLabel("Show");
 | 
				
			||||||
 | 
					        labelSeason = new JLabel("Season");
 | 
				
			||||||
 | 
					        labelEpisode = new JLabel("Episode");
 | 
				
			||||||
 | 
					        labelDuration = new JLabel("Duration");
 | 
				
			||||||
 | 
					        labelProgress = new JLabel("Progress");
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        textTitle = new JTextField();
 | 
				
			||||||
 | 
					        textTitle.setEditable(false);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        textShow = new JTextField();
 | 
				
			||||||
 | 
					        textShow.setEditable(false);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        textSeason = new JTextField();
 | 
				
			||||||
 | 
					        textSeason.setEditable(false);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        textEpisode = new JTextField();
 | 
				
			||||||
 | 
					        textEpisode.setEditable(false);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        textDuration = new JTextField();
 | 
				
			||||||
 | 
					        textDuration.setEditable(false);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        progressBar = new JProgressBar(0, 100);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        setLayout(new GridLayout(6, 2));
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        add(labelTitle);
 | 
				
			||||||
 | 
					        add(textTitle);
 | 
				
			||||||
 | 
					        add(labelShow);
 | 
				
			||||||
 | 
					        add(textShow);
 | 
				
			||||||
 | 
					        add(labelSeason);
 | 
				
			||||||
 | 
					        add(textSeason);
 | 
				
			||||||
 | 
					        add(labelEpisode);
 | 
				
			||||||
 | 
					        add(textEpisode);
 | 
				
			||||||
 | 
					        add(labelDuration);
 | 
				
			||||||
 | 
					        add(textDuration);
 | 
				
			||||||
 | 
					        add(labelProgress);
 | 
				
			||||||
 | 
					        add(progressBar);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public void clearInformation(){
 | 
				
			||||||
 | 
					        this.selectedDownloadTask = null;
 | 
				
			||||||
 | 
					        textTitle.setText("");
 | 
				
			||||||
 | 
					        textShow.setText("");
 | 
				
			||||||
 | 
					        textSeason.setText("");
 | 
				
			||||||
 | 
					        textEpisode.setText("");
 | 
				
			||||||
 | 
					        textDuration.setText("");
 | 
				
			||||||
 | 
					        progressBar.setValue(0);
 | 
				
			||||||
 | 
					        progressBar.setString("");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public void setInformation(DownloadTask downloadTask){
 | 
				
			||||||
 | 
					        textTitle.setText(downloadTask.getEpisodeWrapper().getTitle());
 | 
				
			||||||
 | 
					        textTitle.setEditable(false);
 | 
				
			||||||
 | 
					        textShow.setText(downloadTask.getEpisodeWrapper().getShow());
 | 
				
			||||||
 | 
					        textShow.setEditable(false);
 | 
				
			||||||
 | 
					        textSeason.setText(downloadTask.getEpisodeWrapper().getSeason());
 | 
				
			||||||
 | 
					        textSeason.setEditable(false);
 | 
				
			||||||
 | 
					        textEpisode.setText(downloadTask.getEpisodeWrapper().getEpisode());
 | 
				
			||||||
 | 
					        textEpisode.setEditable(false);
 | 
				
			||||||
 | 
					        textDuration.setText(downloadTask.getEpisodeWrapper().getDuration());
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if(selectedDownloadTask != null && selectedDownloadTask.equals(downloadTask)){
 | 
				
			||||||
 | 
					            progressBar.setValue(downloadTask.percentCompleted);
 | 
				
			||||||
 | 
					            progressBar.setStringPainted(true);
 | 
				
			||||||
 | 
					            String done = FileUtils.byteCountToDisplaySize(downloadTask.totalBytesRead);
 | 
				
			||||||
 | 
					            String todo = FileUtils.byteCountToDisplaySize(downloadTask.fileSize);
 | 
				
			||||||
 | 
					            progressBar.setString(done+"/"+todo);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        downloadTask.addPropertyChangeListener((PropertyChangeEvent evt) -> {
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if(selectedDownloadTask != null && evt.getPropertyName().equals(Long.toString(selectedDownloadTask.getEpisodeWrapper().getId()))){
 | 
				
			||||||
 | 
					                progressBar.setValue((int)evt.getNewValue());
 | 
				
			||||||
 | 
					                progressBar.setStringPainted(true);
 | 
				
			||||||
 | 
					                String done = FileUtils.byteCountToDisplaySize(downloadTask.totalBytesRead);
 | 
				
			||||||
 | 
					                String todo = FileUtils.byteCountToDisplaySize(downloadTask.fileSize);
 | 
				
			||||||
 | 
					                progressBar.setString(done+"/"+todo);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,124 @@
 | 
				
			|||||||
 | 
					package com.greinet.tvtotalripper.ui;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.greinet.tvtotalripper.crawler.EpisodeWrapper;
 | 
				
			||||||
 | 
					import java.awt.Color;
 | 
				
			||||||
 | 
					import java.awt.GridBagConstraints;
 | 
				
			||||||
 | 
					import java.awt.GridBagLayout;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					import javax.swing.BorderFactory;
 | 
				
			||||||
 | 
					import javax.swing.DefaultListModel;
 | 
				
			||||||
 | 
					import javax.swing.JButton;
 | 
				
			||||||
 | 
					import javax.swing.JLabel;
 | 
				
			||||||
 | 
					import javax.swing.JList;
 | 
				
			||||||
 | 
					import javax.swing.JPanel;
 | 
				
			||||||
 | 
					import javax.swing.JScrollPane;
 | 
				
			||||||
 | 
					import javax.swing.JTextField;
 | 
				
			||||||
 | 
					import javax.swing.event.ListSelectionEvent;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author agreiner
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class EpisodesRipperPanel {
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private final JPanel panel;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private JList<EpisodeWrapper> listPanel;
 | 
				
			||||||
 | 
					    private DefaultListModel<EpisodeWrapper> listModel;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private JTextField textField;
 | 
				
			||||||
 | 
					    private final JButton buttonDownload;
 | 
				
			||||||
 | 
					    private final JLabel label;
 | 
				
			||||||
 | 
					    private final List<EpisodeWrapper> episodes;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private final JButton buttonDownloadAll;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public EpisodesRipperPanel(List<EpisodeWrapper> episodes){
 | 
				
			||||||
 | 
					        this.episodes = episodes;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        panel = new JPanel(false);
 | 
				
			||||||
 | 
					        panel.setLayout(new GridBagLayout());
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        GridBagConstraints c = new GridBagConstraints();
 | 
				
			||||||
 | 
					        c.gridx = 0;
 | 
				
			||||||
 | 
					        c.gridy = 0;
 | 
				
			||||||
 | 
					        c.gridwidth = 3;
 | 
				
			||||||
 | 
					        c.fill = GridBagConstraints.BOTH;
 | 
				
			||||||
 | 
					        c.weightx = 0.4;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        textField = new JTextField();
 | 
				
			||||||
 | 
					        textField.setEditable(false);
 | 
				
			||||||
 | 
					        panel.add(textField,c);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        buttonDownload = new JButton("Download");
 | 
				
			||||||
 | 
					        c.gridx = 3;
 | 
				
			||||||
 | 
					        c.gridwidth = 2;
 | 
				
			||||||
 | 
					        c.weightx = 0.1;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        panel.add(buttonDownload,c);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        listModel = new DefaultListModel<>();
 | 
				
			||||||
 | 
					        episodes.forEach(e -> listModel.addElement(e));
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        listPanel = new JList<>(listModel);
 | 
				
			||||||
 | 
					        JScrollPane scrollPane = new JScrollPane();
 | 
				
			||||||
 | 
					        scrollPane.setViewportView(listPanel);
 | 
				
			||||||
 | 
					        listPanel.setLayoutOrientation(JList.VERTICAL);
 | 
				
			||||||
 | 
					        listPanel.setSelectedIndex(0);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        listPanel.addListSelectionListener((ListSelectionEvent e) -> {
 | 
				
			||||||
 | 
					            textField.setText(listPanel.getSelectedValue().toString());
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        textField.setText(listPanel.getSelectedValue().toString());
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        c.gridx = 0;
 | 
				
			||||||
 | 
					        c.gridy = 1;
 | 
				
			||||||
 | 
					        c.gridwidth = 5;
 | 
				
			||||||
 | 
					        c.gridheight = 5;
 | 
				
			||||||
 | 
					        c.weightx = 0.5;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        panel.add(scrollPane,c);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        label = new JLabel(Integer.toString(episodes.size()));
 | 
				
			||||||
 | 
					        label.setBorder(BorderFactory.createLineBorder(Color.BLACK, 1));
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        buttonDownloadAll = new JButton("Download all episodes");
 | 
				
			||||||
 | 
					        c.gridx = 6;
 | 
				
			||||||
 | 
					        c.gridy = 0;
 | 
				
			||||||
 | 
					        c.gridwidth = 1;
 | 
				
			||||||
 | 
					        c.gridheight = 6;
 | 
				
			||||||
 | 
					        c.weightx = 0.5;
 | 
				
			||||||
 | 
					        panel.add(buttonDownloadAll,c);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        c.gridx = 4;
 | 
				
			||||||
 | 
					        c.gridy = 6;
 | 
				
			||||||
 | 
					        c.gridwidth = 1;
 | 
				
			||||||
 | 
					        c.gridheight = 1;
 | 
				
			||||||
 | 
					        c.fill = GridBagConstraints.NONE;
 | 
				
			||||||
 | 
					        c.anchor = GridBagConstraints.EAST;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        panel.add(label, c);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public JPanel getJPanel(){
 | 
				
			||||||
 | 
					        return panel;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public EpisodeWrapper getCurrentSelected(){
 | 
				
			||||||
 | 
					        return listPanel.getSelectedValue();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public JButton getDownloadButton(){
 | 
				
			||||||
 | 
					        return buttonDownload;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public JButton getDownloadAllButton(){
 | 
				
			||||||
 | 
					        return buttonDownloadAll;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										132
									
								
								src/main/java/com/greinet/tvtotalripper/ui/RipperWindow.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								src/main/java/com/greinet/tvtotalripper/ui/RipperWindow.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,132 @@
 | 
				
			|||||||
 | 
					package com.greinet.tvtotalripper.ui;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.greinet.tvtotalripper.crawler.CrawlerUtil;
 | 
				
			||||||
 | 
					import com.greinet.tvtotalripper.crawler.EpisodeWrapper;
 | 
				
			||||||
 | 
					import com.greinet.tvtotalripper.download.DownloadTask;
 | 
				
			||||||
 | 
					import java.awt.Dimension;
 | 
				
			||||||
 | 
					import java.awt.event.ActionEvent;
 | 
				
			||||||
 | 
					import java.awt.event.ActionListener;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					import java.util.stream.Collectors;
 | 
				
			||||||
 | 
					import javax.swing.JComponent;
 | 
				
			||||||
 | 
					import javax.swing.JFrame;
 | 
				
			||||||
 | 
					import javax.swing.JPanel;
 | 
				
			||||||
 | 
					import javax.swing.JTabbedPane;
 | 
				
			||||||
 | 
					import org.openqa.selenium.By;
 | 
				
			||||||
 | 
					import org.openqa.selenium.WebElement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author agreiner
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class RipperWindow {
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private final JFrame frame;
 | 
				
			||||||
 | 
					    private final JTabbedPane tabbedPane;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private JComponent panel1;
 | 
				
			||||||
 | 
					    private JComponent panel2;
 | 
				
			||||||
 | 
					    private JComponent panel3;
 | 
				
			||||||
 | 
					    private JComponent panel4;
 | 
				
			||||||
 | 
					    private JComponent panel5;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private BaseRipperPanel showRipperPanel;
 | 
				
			||||||
 | 
					    private SettingsRipperPanel srp;
 | 
				
			||||||
 | 
					    private DownloadRipperPanel drp;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public RipperWindow(){
 | 
				
			||||||
 | 
					        frame = new JFrame("TV Total Ripper");
 | 
				
			||||||
 | 
					        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        tabbedPane = new JTabbedPane();
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        showRipperPanel = createShowsPanel();
 | 
				
			||||||
 | 
					        srp = new SettingsRipperPanel();
 | 
				
			||||||
 | 
					        drp = new DownloadRipperPanel();
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        panel1 = showRipperPanel.getJPanel();
 | 
				
			||||||
 | 
					        panel2 = new JPanel();
 | 
				
			||||||
 | 
					        panel3 = new JPanel();
 | 
				
			||||||
 | 
					        panel4 = drp.getJPanel();
 | 
				
			||||||
 | 
					        panel5 = srp.getPanel();
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        tabbedPane.addTab("Shows", panel1);
 | 
				
			||||||
 | 
					        tabbedPane.addTab("Seasons", panel2);
 | 
				
			||||||
 | 
					        tabbedPane.addTab("Episodes", panel3);
 | 
				
			||||||
 | 
					        tabbedPane.addTab("Downloads", panel4);
 | 
				
			||||||
 | 
					        tabbedPane.addTab("Settings", panel5);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        tabbedPane.setEnabledAt(1, false);
 | 
				
			||||||
 | 
					        tabbedPane.setEnabledAt(2, false);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        tabbedPane.setPreferredSize(new Dimension(900, 400));
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        frame.add(tabbedPane);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        frame.pack();
 | 
				
			||||||
 | 
					        frame.setAlwaysOnTop(true);
 | 
				
			||||||
 | 
					        frame.setVisible(true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public static void main(String[] args) {
 | 
				
			||||||
 | 
					        RipperWindow ripperWindow = new RipperWindow();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private BaseRipperPanel createShowsPanel(){
 | 
				
			||||||
 | 
					        List<WebElement> shows = CrawlerUtil.getShows();
 | 
				
			||||||
 | 
					        Map<String, String> namesToUrls = shows.stream().collect(Collectors.toMap(e -> e.findElement(By.xpath(".//img")).getAttribute("alt") , e -> e.getAttribute("href")));
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        BaseRipperPanel brp = new BaseRipperPanel(namesToUrls);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        brp.getJButton().addActionListener((ActionEvent e) -> {
 | 
				
			||||||
 | 
					            panel3 = new JPanel();
 | 
				
			||||||
 | 
					            tabbedPane.setEnabledAt(2, false);
 | 
				
			||||||
 | 
					            panel1 = createSeasonsPanel(brp.getCurrentSelected()).getJPanel();
 | 
				
			||||||
 | 
					            tabbedPane.setComponentAt(1, panel1);
 | 
				
			||||||
 | 
					            tabbedPane.setEnabledAt(1, true);
 | 
				
			||||||
 | 
					            tabbedPane.setSelectedIndex(1);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        return brp;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private BaseRipperPanel createSeasonsPanel(String url){
 | 
				
			||||||
 | 
					        List<WebElement> seasons = CrawlerUtil.getSeasons(url);
 | 
				
			||||||
 | 
					        Map<String, String> elements = seasons.stream().collect(Collectors.toMap(e -> e.getText(), e -> e.getText()));
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        BaseRipperPanel brp = new BaseRipperPanel(elements);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        brp.getJButton().addActionListener((ActionEvent e) -> {
 | 
				
			||||||
 | 
					            tabbedPane.setEnabledAt(2, false);
 | 
				
			||||||
 | 
					            panel2 = createEpisodesPanel(url,brp.getCurrentSelected()).getJPanel();
 | 
				
			||||||
 | 
					            tabbedPane.setComponentAt(2, panel2);
 | 
				
			||||||
 | 
					            tabbedPane.setEnabledAt(2, true);
 | 
				
			||||||
 | 
					            tabbedPane.setSelectedIndex(2);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        return brp;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private EpisodesRipperPanel createEpisodesPanel(String url, String seasonName){
 | 
				
			||||||
 | 
					        List<EpisodeWrapper> episodes = CrawlerUtil.getEpisodes(url, seasonName);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        EpisodesRipperPanel erp = new EpisodesRipperPanel(episodes);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        erp.getDownloadButton().addActionListener(new ActionListener() {
 | 
				
			||||||
 | 
					            @Override
 | 
				
			||||||
 | 
					            public void actionPerformed(ActionEvent e) {
 | 
				
			||||||
 | 
					                drp.addTask(new DownloadTask(erp.getCurrentSelected()));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        erp.getDownloadAllButton().addActionListener(new ActionListener() {
 | 
				
			||||||
 | 
					            @Override
 | 
				
			||||||
 | 
					            public void actionPerformed(ActionEvent e) {
 | 
				
			||||||
 | 
					                episodes.forEach(d -> drp.addTask(new DownloadTask(d)));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        return erp;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,90 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * To change this license header, choose License Headers in Project Properties.
 | 
				
			||||||
 | 
					 * To change this template file, choose Tools | Templates
 | 
				
			||||||
 | 
					 * and open the template in the editor.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					package com.greinet.tvtotalripper.ui;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.awt.Dimension;
 | 
				
			||||||
 | 
					import java.awt.Font;
 | 
				
			||||||
 | 
					import java.awt.GridBagConstraints;
 | 
				
			||||||
 | 
					import java.awt.GridBagLayout;
 | 
				
			||||||
 | 
					import java.io.File;
 | 
				
			||||||
 | 
					import javax.swing.JButton;
 | 
				
			||||||
 | 
					import javax.swing.JFileChooser;
 | 
				
			||||||
 | 
					import javax.swing.JLabel;
 | 
				
			||||||
 | 
					import javax.swing.JPanel;
 | 
				
			||||||
 | 
					import javax.swing.JTextField;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author agreiner
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class SettingsRipperPanel {
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private JPanel panel;
 | 
				
			||||||
 | 
					    private JLabel labelDownloadFolder;
 | 
				
			||||||
 | 
					    private JTextField textDownloadFolder;
 | 
				
			||||||
 | 
					    private JFileChooser fileChooserDownloadFolder;
 | 
				
			||||||
 | 
					    public static File DOWNLOADFOLDER = new File(".");
 | 
				
			||||||
 | 
					    private JButton button;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public SettingsRipperPanel(){
 | 
				
			||||||
 | 
					        panel = new JPanel();
 | 
				
			||||||
 | 
					        panel.setLayout(new GridBagLayout());
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        GridBagConstraints gbc = new GridBagConstraints();
 | 
				
			||||||
 | 
					        gbc.anchor = GridBagConstraints.NORTHWEST;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        gbc.gridx=0;
 | 
				
			||||||
 | 
					        gbc.gridy=0;
 | 
				
			||||||
 | 
					        gbc.gridwidth=1;
 | 
				
			||||||
 | 
					        gbc.insets.bottom = 10;
 | 
				
			||||||
 | 
					        labelDownloadFolder = new JLabel("Download folder");
 | 
				
			||||||
 | 
					        labelDownloadFolder.setFont(new Font(labelDownloadFolder.getFont().getFontName(), Font.BOLD, 15));
 | 
				
			||||||
 | 
					        panel.add(labelDownloadFolder, gbc);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        gbc.gridx=0;
 | 
				
			||||||
 | 
					        gbc.gridy=1;
 | 
				
			||||||
 | 
					        gbc.gridwidth=8;
 | 
				
			||||||
 | 
					        gbc.insets.left = 20;
 | 
				
			||||||
 | 
					        textDownloadFolder = new JTextField(DOWNLOADFOLDER.getAbsolutePath());
 | 
				
			||||||
 | 
					        textDownloadFolder.setEditable(false);
 | 
				
			||||||
 | 
					        textDownloadFolder.setPreferredSize(new Dimension(500, 30));
 | 
				
			||||||
 | 
					        panel.add(textDownloadFolder,gbc);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        gbc.gridx=8;
 | 
				
			||||||
 | 
					        gbc.gridy=1;
 | 
				
			||||||
 | 
					        gbc.gridwidth=1;
 | 
				
			||||||
 | 
					        gbc.insets.left = 0;
 | 
				
			||||||
 | 
					        button = new JButton("...");
 | 
				
			||||||
 | 
					        panel.add(button,gbc);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        fileChooserDownloadFolder = new JFileChooser(DOWNLOADFOLDER);
 | 
				
			||||||
 | 
					        fileChooserDownloadFolder.setDialogTitle("Select download folder");
 | 
				
			||||||
 | 
					        fileChooserDownloadFolder.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
 | 
				
			||||||
 | 
					        fileChooserDownloadFolder.setAcceptAllFileFilterUsed(false);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        button.addActionListener(e -> {
 | 
				
			||||||
 | 
					            int value = fileChooserDownloadFolder.showOpenDialog(panel);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if (value == JFileChooser.APPROVE_OPTION){
 | 
				
			||||||
 | 
					                DOWNLOADFOLDER = fileChooserDownloadFolder.getSelectedFile();
 | 
				
			||||||
 | 
					                textDownloadFolder.setText(DOWNLOADFOLDER.getAbsolutePath());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public JPanel getPanel() {
 | 
				
			||||||
 | 
					        return panel;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user