- Deleted unused classes
- Refined and added javadoc - Disabled MetaDataWriter
This commit is contained in:
parent
34ea601a95
commit
f8dd7be711
@ -1,17 +1,46 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<actions>
|
<actions>
|
||||||
<action>
|
<action>
|
||||||
<actionName>run</actionName>
|
<actionName>run</actionName>
|
||||||
<packagings>
|
<packagings>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
</packagings>
|
</packagings>
|
||||||
<goals>
|
<goals>
|
||||||
<goal>process-classes</goal>
|
<goal>process-classes</goal>
|
||||||
<goal>org.codehaus.mojo:exec-maven-plugin:1.5.0:exec</goal>
|
<goal>org.codehaus.mojo:exec-maven-plugin:1.5.0:exec</goal>
|
||||||
</goals>
|
</goals>
|
||||||
<properties>
|
<properties>
|
||||||
<exec.args>-classpath %classpath com.greinet.tvtotalripper.Main</exec.args>
|
<exec.args>-classpath %classpath com.greinet.tvtotalripper.ui.RipperWindow</exec.args>
|
||||||
<exec.executable>java</exec.executable>
|
<exec.executable>java</exec.executable>
|
||||||
</properties>
|
</properties>
|
||||||
</action>
|
</action>
|
||||||
</actions>
|
<action>
|
||||||
|
<actionName>debug</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>-agentlib:jdwp=transport=dt_socket,server=n,address=${jpda.address} -classpath %classpath com.greinet.tvtotalripper.ui.RipperWindow</exec.args>
|
||||||
|
<exec.executable>java</exec.executable>
|
||||||
|
<jpda.listen>true</jpda.listen>
|
||||||
|
</properties>
|
||||||
|
</action>
|
||||||
|
<action>
|
||||||
|
<actionName>profile</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.ui.RipperWindow</exec.args>
|
||||||
|
<exec.executable>java</exec.executable>
|
||||||
|
</properties>
|
||||||
|
</action>
|
||||||
|
</actions>
|
||||||
|
@ -1,172 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,189 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -1,231 +1,234 @@
|
|||||||
package com.greinet.tvtotalripper;
|
package com.greinet.tvtotalripper;
|
||||||
|
|
||||||
|
import org.mp4parser.Box;
|
||||||
import org.mp4parser.Box;
|
import org.mp4parser.Container;
|
||||||
import org.mp4parser.Container;
|
import org.mp4parser.IsoFile;
|
||||||
import org.mp4parser.IsoFile;
|
import org.mp4parser.boxes.apple.AppleItemListBox;
|
||||||
import org.mp4parser.boxes.apple.AppleItemListBox;
|
import org.mp4parser.boxes.apple.AppleNameBox;
|
||||||
import org.mp4parser.boxes.apple.AppleNameBox;
|
import org.mp4parser.boxes.iso14496.part12.*;
|
||||||
import org.mp4parser.boxes.iso14496.part12.*;
|
import org.mp4parser.tools.Path;
|
||||||
import org.mp4parser.tools.Path;
|
|
||||||
|
import java.io.*;
|
||||||
import java.io.*;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.channels.Channels;
|
||||||
import java.nio.channels.Channels;
|
import java.nio.channels.FileChannel;
|
||||||
import java.nio.channels.FileChannel;
|
import java.util.List;
|
||||||
import java.util.List;
|
import org.mp4parser.boxes.apple.AppleArtistBox;
|
||||||
import org.mp4parser.boxes.apple.AppleArtistBox;
|
import org.mp4parser.boxes.apple.AppleTVEpisodeNumberBox;
|
||||||
import org.mp4parser.boxes.apple.AppleTVEpisodeBox;
|
import org.mp4parser.boxes.apple.AppleTVShowBox;
|
||||||
import org.mp4parser.boxes.apple.AppleTVEpisodeNumberBox;
|
|
||||||
import org.mp4parser.boxes.apple.AppleTVSeasonBox;
|
/**
|
||||||
import org.mp4parser.boxes.apple.AppleTVShowBox;
|
* Utility class to change mp4 metatdata, more or less copied from https://github.com/sannies/mp4parser/blob/master/examples/src/main/java/org/mp4parser/examples/metadata/MetaDataInsert.java
|
||||||
|
*/
|
||||||
/**
|
public class MetaDataWriter {
|
||||||
* Change metadata and make sure chunkoffsets are corrected.
|
|
||||||
*/
|
private FileChannel splitFileAndInsert(File f, long pos, long length) throws IOException {
|
||||||
public class MetaDataWriter {
|
FileChannel read = new RandomAccessFile(f, "r").getChannel();
|
||||||
|
File tmp = File.createTempFile("ChangeMetaData", "splitFileAndInsert");
|
||||||
private FileChannel splitFileAndInsert(File f, long pos, long length) throws IOException {
|
FileChannel tmpWrite = new RandomAccessFile(tmp, "rw").getChannel();
|
||||||
FileChannel read = new RandomAccessFile(f, "r").getChannel();
|
read.position(pos);
|
||||||
File tmp = File.createTempFile("ChangeMetaData", "splitFileAndInsert");
|
tmpWrite.transferFrom(read, 0, read.size() - pos);
|
||||||
FileChannel tmpWrite = new RandomAccessFile(tmp, "rw").getChannel();
|
read.close();
|
||||||
read.position(pos);
|
FileChannel write = new RandomAccessFile(f, "rw").getChannel();
|
||||||
tmpWrite.transferFrom(read, 0, read.size() - pos);
|
write.position(pos + length);
|
||||||
read.close();
|
tmpWrite.position(0);
|
||||||
FileChannel write = new RandomAccessFile(f, "rw").getChannel();
|
long transferred = 0;
|
||||||
write.position(pos + length);
|
while ((transferred += tmpWrite.transferTo(0, tmpWrite.size() - transferred, write)) != tmpWrite.size()) {
|
||||||
tmpWrite.position(0);
|
}
|
||||||
long transferred = 0;
|
tmpWrite.close();
|
||||||
while ((transferred += tmpWrite.transferTo(0, tmpWrite.size() - transferred, write)) != tmpWrite.size()) {
|
tmp.delete();
|
||||||
}
|
return write;
|
||||||
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;
|
||||||
private boolean needsOffsetCorrection(IsoFile isoFile) {
|
} else {
|
||||||
if (Path.getPath(isoFile, "moov[0]/mvex[0]") != null) {
|
// no correction needed if mdat is before moov as insert into moov want change the offsets of mdat
|
||||||
// Fragmented files don't need a correction
|
for (Box box : isoFile.getBoxes()) {
|
||||||
return false;
|
if ("moov".equals(box.getType())) {
|
||||||
} else {
|
return true;
|
||||||
// no correction needed if mdat is before moov as insert into moov want change the offsets of mdat
|
}
|
||||||
for (Box box : isoFile.getBoxes()) {
|
if ("mdat".equals(box.getType())) {
|
||||||
if ("moov".equals(box.getType())) {
|
return false;
|
||||||
return true;
|
}
|
||||||
}
|
}
|
||||||
if ("mdat".equals(box.getType())) {
|
throw new RuntimeException("I need moov or mdat. Otherwise all this doesn't make sense");
|
||||||
return false;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
throw new RuntimeException("I need moov or mdat. Otherwise all this doesn't make sense");
|
/**
|
||||||
}
|
* Write itunes metadata to a mp4 file
|
||||||
}
|
* @param videoFilePath the file path
|
||||||
|
* @param title the title, or <code>null</code>
|
||||||
public void writeMetadata(String videoFilePath, String title, String artist, String show, String episode) throws IOException {
|
* @param artist the artist, or <code>null</code>
|
||||||
|
* @param show the show, or <code>null</code>
|
||||||
File videoFile = new File(videoFilePath);
|
* @param episode the episode, or <code>null</code>
|
||||||
if (!videoFile.exists()) {
|
* @throws IOException error heandling mp4 file
|
||||||
throw new FileNotFoundException("File " + videoFilePath + " not exists");
|
*/
|
||||||
}
|
public void writeMetadata(String videoFilePath, String title, String artist, String show, String episode) throws IOException {
|
||||||
|
|
||||||
if (!videoFile.canWrite()) {
|
File videoFile = new File(videoFilePath);
|
||||||
throw new IllegalStateException("No write permissions to file " + videoFilePath);
|
if (!videoFile.exists()) {
|
||||||
}
|
throw new FileNotFoundException("File " + videoFilePath + " not exists");
|
||||||
IsoFile isoFile = new IsoFile(videoFilePath);
|
}
|
||||||
|
|
||||||
MovieBox moov = isoFile.getBoxes(MovieBox.class).get(0);
|
if (!videoFile.canWrite()) {
|
||||||
FreeBox freeBox = findFreeBox(moov);
|
throw new IllegalStateException("No write permissions to file " + videoFilePath);
|
||||||
|
}
|
||||||
boolean correctOffset = needsOffsetCorrection(isoFile);
|
IsoFile isoFile = new IsoFile(videoFilePath);
|
||||||
long sizeBefore = moov.getSize();
|
|
||||||
long offset = 0;
|
MovieBox moov = isoFile.getBoxes(MovieBox.class).get(0);
|
||||||
for (Box box : isoFile.getBoxes()) {
|
FreeBox freeBox = findFreeBox(moov);
|
||||||
if ("moov".equals(box.getType())) {
|
|
||||||
break;
|
boolean correctOffset = needsOffsetCorrection(isoFile);
|
||||||
}
|
long sizeBefore = moov.getSize();
|
||||||
offset += box.getSize();
|
long offset = 0;
|
||||||
}
|
for (Box box : isoFile.getBoxes()) {
|
||||||
|
if ("moov".equals(box.getType())) {
|
||||||
// Create structure or just navigate to Apple List Box.
|
break;
|
||||||
UserDataBox userDataBox;
|
}
|
||||||
if ((userDataBox = Path.getPath(moov, "udta")) == null) {
|
offset += box.getSize();
|
||||||
userDataBox = new UserDataBox();
|
}
|
||||||
moov.addBox(userDataBox);
|
|
||||||
}
|
// Create structure or just navigate to Apple List Box.
|
||||||
MetaBox metaBox;
|
UserDataBox userDataBox;
|
||||||
if ((metaBox = Path.getPath(userDataBox, "meta")) == null) {
|
if ((userDataBox = Path.getPath(moov, "udta")) == null) {
|
||||||
metaBox = new MetaBox();
|
userDataBox = new UserDataBox();
|
||||||
HandlerBox hdlr = new HandlerBox();
|
moov.addBox(userDataBox);
|
||||||
hdlr.setHandlerType("mdir");
|
}
|
||||||
metaBox.addBox(hdlr);
|
MetaBox metaBox;
|
||||||
userDataBox.addBox(metaBox);
|
if ((metaBox = Path.getPath(userDataBox, "meta")) == null) {
|
||||||
}
|
metaBox = new MetaBox();
|
||||||
AppleItemListBox ilst;
|
HandlerBox hdlr = new HandlerBox();
|
||||||
if ((ilst = Path.getPath(metaBox, "ilst")) == null) {
|
hdlr.setHandlerType("mdir");
|
||||||
ilst = new AppleItemListBox();
|
metaBox.addBox(hdlr);
|
||||||
metaBox.addBox(ilst);
|
userDataBox.addBox(metaBox);
|
||||||
|
}
|
||||||
}
|
AppleItemListBox ilst;
|
||||||
if (freeBox == null) {
|
if ((ilst = Path.getPath(metaBox, "ilst")) == null) {
|
||||||
freeBox = new FreeBox(128 * 1024);
|
ilst = new AppleItemListBox();
|
||||||
metaBox.addBox(freeBox);
|
metaBox.addBox(ilst);
|
||||||
}
|
|
||||||
|
}
|
||||||
if(title != null){
|
if (freeBox == null) {
|
||||||
// Write the title
|
freeBox = new FreeBox(128 * 1024);
|
||||||
AppleNameBox nam;
|
metaBox.addBox(freeBox);
|
||||||
if ((nam = Path.getPath(ilst, "©nam")) == null) {
|
}
|
||||||
nam = new AppleNameBox();
|
|
||||||
}
|
if(title != null){
|
||||||
nam.setDataCountry(0);
|
// Write the title
|
||||||
nam.setDataLanguage(0);
|
AppleNameBox nam;
|
||||||
nam.setValue(title);
|
if ((nam = Path.getPath(ilst, "©nam")) == null) {
|
||||||
ilst.addBox(nam);
|
nam = new AppleNameBox();
|
||||||
}
|
}
|
||||||
|
nam.setDataCountry(0);
|
||||||
if(artist!=null){
|
nam.setDataLanguage(0);
|
||||||
// Write the artist
|
nam.setValue(title);
|
||||||
AppleArtistBox art;
|
ilst.addBox(nam);
|
||||||
if ((art = Path.getPath(ilst, "©ART")) == null) {
|
}
|
||||||
art = new AppleArtistBox();
|
|
||||||
}
|
if(artist!=null){
|
||||||
art.setDataCountry(0);
|
// Write the artist
|
||||||
art.setDataLanguage(0);
|
AppleArtistBox art;
|
||||||
art.setValue(artist);
|
if ((art = Path.getPath(ilst, "©ART")) == null) {
|
||||||
ilst.addBox(art);
|
art = new AppleArtistBox();
|
||||||
}
|
}
|
||||||
|
art.setDataCountry(0);
|
||||||
if(episode!=null){
|
art.setDataLanguage(0);
|
||||||
// Write the episode title
|
art.setValue(artist);
|
||||||
AppleTVEpisodeNumberBox ep;
|
ilst.addBox(art);
|
||||||
if ((ep = Path.getPath(ilst, "tven")) == null) {
|
}
|
||||||
ep = new AppleTVEpisodeNumberBox();
|
|
||||||
}
|
if(episode!=null){
|
||||||
ep.setDataCountry(0);
|
// Write the episode title
|
||||||
ep.setDataLanguage(0);
|
AppleTVEpisodeNumberBox ep;
|
||||||
ep.setValue(episode);
|
if ((ep = Path.getPath(ilst, "tven")) == null) {
|
||||||
ilst.addBox(ep);
|
ep = new AppleTVEpisodeNumberBox();
|
||||||
}
|
}
|
||||||
|
ep.setDataCountry(0);
|
||||||
if(show!=null){
|
ep.setDataLanguage(0);
|
||||||
//Write the show
|
ep.setValue(episode);
|
||||||
AppleTVShowBox sh;
|
ilst.addBox(ep);
|
||||||
if ((sh = Path.getPath(ilst, "tvsh")) == null) {
|
}
|
||||||
sh = new AppleTVShowBox();
|
|
||||||
}
|
if(show!=null){
|
||||||
sh.setDataCountry(0);
|
//Write the show
|
||||||
sh.setDataLanguage(0);
|
AppleTVShowBox sh;
|
||||||
sh.setValue(show);
|
if ((sh = Path.getPath(ilst, "tvsh")) == null) {
|
||||||
ilst.addBox(sh);
|
sh = new AppleTVShowBox();
|
||||||
}
|
}
|
||||||
|
sh.setDataCountry(0);
|
||||||
|
sh.setDataLanguage(0);
|
||||||
|
sh.setValue(show);
|
||||||
long sizeAfter = moov.getSize();
|
ilst.addBox(sh);
|
||||||
long diff = sizeAfter - sizeBefore;
|
}
|
||||||
// This is the difference of before/after
|
|
||||||
|
long sizeAfter = moov.getSize();
|
||||||
// can we compensate by resizing a Free Box we have found?
|
long diff = sizeAfter - sizeBefore;
|
||||||
if (freeBox.getData().limit() > diff) {
|
// This is the difference of before/after
|
||||||
// either shrink or grow!
|
|
||||||
freeBox.setData(ByteBuffer.allocate((int) (freeBox.getData().limit() - diff)));
|
// can we compensate by resizing a Free Box we have found?
|
||||||
sizeAfter = moov.getSize();
|
if (freeBox.getData().limit() > diff) {
|
||||||
diff = sizeAfter - sizeBefore;
|
// either shrink or grow!
|
||||||
}
|
freeBox.setData(ByteBuffer.allocate((int) (freeBox.getData().limit() - diff)));
|
||||||
if (correctOffset && diff != 0) {
|
sizeAfter = moov.getSize();
|
||||||
correctChunkOffsets(moov, diff);
|
diff = sizeAfter - sizeBefore;
|
||||||
}
|
}
|
||||||
BetterByteArrayOutputStream baos = new BetterByteArrayOutputStream();
|
if (correctOffset && diff != 0) {
|
||||||
moov.getBox(Channels.newChannel(baos));
|
correctChunkOffsets(moov, diff);
|
||||||
isoFile.close();
|
}
|
||||||
FileChannel fc;
|
BetterByteArrayOutputStream baos = new BetterByteArrayOutputStream();
|
||||||
if (diff != 0) {
|
moov.getBox(Channels.newChannel(baos));
|
||||||
// this is not good: We have to insert bytes in the middle of the file
|
isoFile.close();
|
||||||
// and this costs time as it requires re-writing most of the file's data
|
FileChannel fc;
|
||||||
fc = splitFileAndInsert(videoFile, offset, sizeAfter - sizeBefore);
|
if (diff != 0) {
|
||||||
} else {
|
// this is not good: We have to insert bytes in the middle of the file
|
||||||
// simple overwrite of something with the file
|
// and this costs time as it requires re-writing most of the file's data
|
||||||
fc = new RandomAccessFile(videoFile, "rw").getChannel();
|
fc = splitFileAndInsert(videoFile, offset, sizeAfter - sizeBefore);
|
||||||
}
|
} else {
|
||||||
fc.position(offset);
|
// simple overwrite of something with the file
|
||||||
fc.write(ByteBuffer.wrap(baos.getBuffer(), 0, baos.size()));
|
fc = new RandomAccessFile(videoFile, "rw").getChannel();
|
||||||
fc.close();
|
}
|
||||||
}
|
fc.position(offset);
|
||||||
|
fc.write(ByteBuffer.wrap(baos.getBuffer(), 0, baos.size()));
|
||||||
FreeBox findFreeBox(Container c) {
|
fc.close();
|
||||||
for (Box box : c.getBoxes()) {
|
}
|
||||||
if (box instanceof FreeBox) {
|
|
||||||
return (FreeBox) box;
|
private FreeBox findFreeBox(Container c) {
|
||||||
}
|
for (Box box : c.getBoxes()) {
|
||||||
if (box instanceof Container) {
|
if (box instanceof FreeBox) {
|
||||||
FreeBox freeBox = findFreeBox((Container) box);
|
return (FreeBox) box;
|
||||||
if (freeBox != null) {
|
}
|
||||||
return freeBox;
|
if (box instanceof Container) {
|
||||||
}
|
FreeBox freeBox = findFreeBox((Container) box);
|
||||||
}
|
if (freeBox != null) {
|
||||||
}
|
return freeBox;
|
||||||
return null;
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
private void correctChunkOffsets(MovieBox movieBox, long correction) {
|
return null;
|
||||||
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]");
|
private void correctChunkOffsets(MovieBox movieBox, long correction) {
|
||||||
}
|
List<ChunkOffsetBox> chunkOffsetBoxes = Path.getPaths((Box) movieBox, "trak/mdia[0]/minf[0]/stbl[0]/stco[0]");
|
||||||
for (ChunkOffsetBox chunkOffsetBox : chunkOffsetBoxes) {
|
if (chunkOffsetBoxes.isEmpty()) {
|
||||||
long[] cOffsets = chunkOffsetBox.getChunkOffsets();
|
chunkOffsetBoxes = Path.getPaths((Box) movieBox, "trak/mdia[0]/minf[0]/stbl[0]/st64[0]");
|
||||||
for (int i = 0; i < cOffsets.length; i++) {
|
}
|
||||||
cOffsets[i] += correction;
|
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;
|
|
||||||
}
|
private static class BetterByteArrayOutputStream extends ByteArrayOutputStream {
|
||||||
}
|
byte[] getBuffer() {
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -1,77 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,179 +1,189 @@
|
|||||||
package com.greinet.tvtotalripper.crawler;
|
package com.greinet.tvtotalripper.crawler;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import org.apache.logging.log4j.Level;
|
import org.apache.logging.log4j.Level;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.openqa.selenium.By;
|
import org.openqa.selenium.By;
|
||||||
import org.openqa.selenium.WebDriver;
|
import org.openqa.selenium.WebDriver;
|
||||||
import org.openqa.selenium.WebElement;
|
import org.openqa.selenium.WebElement;
|
||||||
import org.openqa.selenium.chrome.ChromeDriver;
|
import org.openqa.selenium.chrome.ChromeDriver;
|
||||||
import org.openqa.selenium.chrome.ChromeOptions;
|
import org.openqa.selenium.chrome.ChromeOptions;
|
||||||
import org.openqa.selenium.support.ui.ExpectedConditions;
|
import org.openqa.selenium.support.ui.ExpectedConditions;
|
||||||
import org.openqa.selenium.support.ui.WebDriverWait;
|
import org.openqa.selenium.support.ui.WebDriverWait;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class to get show, season und episode elements from the website
|
* Helper class to get show, season und episode elements from the website
|
||||||
* @author agreiner
|
* @author agreiner
|
||||||
*/
|
*/
|
||||||
public class CrawlerUtil {
|
public class CrawlerUtil {
|
||||||
|
|
||||||
static{
|
static{
|
||||||
System.setProperty("webdriver.chrome.driver", "resources/chromedriver.exe");
|
System.setProperty("webdriver.chrome.driver", "resources/chromedriver.exe");
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The logger */
|
/** The logger */
|
||||||
private static final Logger logger = LogManager.getLogger(CrawlerUtil.class);
|
private static final Logger logger = LogManager.getLogger(CrawlerUtil.class);
|
||||||
/** The WebDriver for show information */
|
/** The WebDriver for show information */
|
||||||
private final static WebDriver driverShows = createShowDriver();
|
private final static WebDriver driverShows = createShowDriver();
|
||||||
/** The WebDriver for season and episode information */
|
/** The WebDriver for season and episode information */
|
||||||
private final static WebDriver driverSeasonsAndEpisodes = createChromeDriver();
|
private final static WebDriver driverSeasonsAndEpisodes = createChromeDriver();
|
||||||
/** The WebDriver for fetchfile.net URL conversion */
|
/** The WebDriver for fetchfile.net URL conversion */
|
||||||
private final static WebDriver driverFetchFile = createFetchFileDriver();
|
private final static WebDriver driverFetchFile = createFetchFileDriver();
|
||||||
|
/** The show */
|
||||||
public static String show = "";
|
private static String show = "";
|
||||||
/**
|
|
||||||
* Create a basic ChromeDriver
|
/**
|
||||||
* @return
|
* Create a basic ChromeDriver
|
||||||
*/
|
* @return the WebDriver
|
||||||
private static WebDriver createChromeDriver(){
|
*/
|
||||||
ChromeOptions options = new ChromeOptions();
|
private static WebDriver createChromeDriver(){
|
||||||
options.addArguments("start-maximized");
|
ChromeOptions options = new ChromeOptions();
|
||||||
return new ChromeDriver(options);
|
options.addArguments("start-maximized");
|
||||||
}
|
return new ChromeDriver(options);
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Create the WebDriver to get show information
|
/**
|
||||||
* @return the WebDriver
|
* Create the WebDriver to get show information
|
||||||
*/
|
* @return the WebDriver
|
||||||
private static WebDriver createShowDriver(){
|
*/
|
||||||
WebDriver driver = createChromeDriver();
|
private static WebDriver createShowDriver(){
|
||||||
driver.get("https://www.myspass.de/sendungen-a-bis-z/");
|
WebDriver driver = createChromeDriver();
|
||||||
return driver;
|
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");
|
* Create the WebDriver to get the video URLs from FetchFile.net
|
||||||
return driver;
|
* @return the WebDriver
|
||||||
}
|
*/
|
||||||
|
private static WebDriver createFetchFileDriver(){
|
||||||
/**
|
WebDriver driver = createChromeDriver();
|
||||||
* Navigate to the given URL or do nothing, if already at that URL
|
driver.get("https://de.fetchfile.net/herunterladen-von-myspass");
|
||||||
* @param url the URL
|
return driver;
|
||||||
*/
|
}
|
||||||
private static void navigateToShow(String url){
|
|
||||||
if(!driverSeasonsAndEpisodes.getCurrentUrl().equals(url)){
|
/**
|
||||||
driverSeasonsAndEpisodes.get(url);
|
* Navigate to the given URL or do nothing, if already at that URL
|
||||||
}
|
* @param url the URL
|
||||||
driverSeasonsAndEpisodes.findElement(By.xpath("/html/body/div[4]/div[1]/div[2]/ul/li[2]/a")).click();
|
*/
|
||||||
}
|
private static void navigateToShow(String url){
|
||||||
|
if(!driverSeasonsAndEpisodes.getCurrentUrl().equals(url)){
|
||||||
/**
|
driverSeasonsAndEpisodes.get(url);
|
||||||
* Returns the WebElements representing the shows.
|
}
|
||||||
* @return the show WebElements
|
driverSeasonsAndEpisodes.findElement(By.xpath("/html/body/div[4]/div[1]/div[2]/ul/li[2]/a")).click();
|
||||||
*/
|
}
|
||||||
public static List<WebElement> getShows(){
|
|
||||||
return driverShows.findElements(By.xpath("/html/body/div[5]/div/div/div/div/a"));
|
/**
|
||||||
}
|
* Returns the WebElements representing the shows.
|
||||||
|
* @return the show WebElements
|
||||||
/**
|
*/
|
||||||
* Returns the Webelements represeting the seasons of a specific show
|
public static List<WebElement> getShows(){
|
||||||
* @param url the URL to the show
|
return driverShows.findElements(By.xpath("/html/body/div[5]/div/div/div/div/a"));
|
||||||
* @return the season WebElements
|
}
|
||||||
*/
|
|
||||||
public static List<WebElement> getSeasons(String url){
|
/**
|
||||||
navigateToShow(url);
|
* Returns the Webelements represeting the seasons of a specific show
|
||||||
|
* @param url the URL to the show
|
||||||
String urlNotTrailingSlash = url.substring(0,url.lastIndexOf("/"));
|
* @return the season WebElements
|
||||||
|
*/
|
||||||
show = urlNotTrailingSlash.substring(urlNotTrailingSlash.lastIndexOf("/")+1, urlNotTrailingSlash.length());
|
public static List<WebElement> getSeasons(String url){
|
||||||
return driverSeasonsAndEpisodes.findElements(By.xpath("/html/body/div[4]/div[1]/div[3]/div[2]/div[1]/select/option"));
|
navigateToShow(url);
|
||||||
|
|
||||||
}
|
String urlNotTrailingSlash = url.substring(0,url.lastIndexOf("/"));
|
||||||
|
|
||||||
/**
|
show = urlNotTrailingSlash.substring(urlNotTrailingSlash.lastIndexOf("/")+1, urlNotTrailingSlash.length());
|
||||||
* Returns the information from the episodes from a specific show and season packed into a wrapper class
|
return driverSeasonsAndEpisodes.findElements(By.xpath("/html/body/div[4]/div[1]/div[3]/div[2]/div[1]/select/option"));
|
||||||
* @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){
|
* Returns the information from the episodes from a specific show and season packed into a wrapper class
|
||||||
|
* @param url the URL of the show
|
||||||
// navigate the WebDriver to the specified URL
|
* @param seasonName the name of the season
|
||||||
navigateToShow(url);
|
* @return the list of episodes
|
||||||
|
*/
|
||||||
// Search the specified season
|
public static List<EpisodeWrapper> getEpisodes(String url, String seasonName){
|
||||||
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();
|
// navigate the WebDriver to the specified URL
|
||||||
|
navigateToShow(url);
|
||||||
// If the season was found, navigate there
|
|
||||||
if(seasonElement.isPresent()){
|
// Search the specified season
|
||||||
seasonElement.get().click();
|
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();
|
||||||
// Wait for the website to load and then switch to the list view
|
|
||||||
try {
|
// If the season was found, navigate there
|
||||||
Thread.sleep(2000);
|
if(seasonElement.isPresent()){
|
||||||
} catch (InterruptedException ex) {
|
seasonElement.get().click();
|
||||||
logger.log(Level.ERROR, "Could not find list view button for url:[{0}], season:[{1}]",url, seasonName);
|
|
||||||
}
|
// Wait for the website to load and then switch to the list view
|
||||||
driverSeasonsAndEpisodes.findElement(By.className("listView--toggle_input")).click();
|
try {
|
||||||
|
Thread.sleep(2000);
|
||||||
// Get the parent div of the episode information list
|
} catch (InterruptedException ex) {
|
||||||
List<WebElement> episodeListDivs = driverSeasonsAndEpisodes.findElements(By.xpath("/html/body/div[4]/div[1]/div[3]/div[2]/div"));
|
logger.log(Level.ERROR, "Could not find list view button for url:[{0}], season:[{1}]",url, seasonName);
|
||||||
List<WebElement> filteredNoDisplay = episodeListDivs.stream().filter(e -> !"none".equals(e.getCssValue("display"))).collect(Collectors.toList());
|
}
|
||||||
|
driverSeasonsAndEpisodes.findElement(By.className("listView--toggle_input")).click();
|
||||||
|
|
||||||
if(!filteredNoDisplay.isEmpty()){
|
// Get the parent div of the episode information list
|
||||||
// Get the Webelements with the link and title information
|
List<WebElement> episodeListDivs = driverSeasonsAndEpisodes.findElements(By.xpath("/html/body/div[4]/div[1]/div[3]/div[2]/div"));
|
||||||
List<WebElement> linksToEpisodes = new ArrayList<>();
|
List<WebElement> filteredNoDisplay = episodeListDivs.stream().filter(e -> !"none".equals(e.getCssValue("display"))).collect(Collectors.toList());
|
||||||
filteredNoDisplay.forEach(e ->
|
|
||||||
linksToEpisodes.addAll(e.findElements(By.xpath(".//div/table/tbody/tr/td/a")))
|
|
||||||
);
|
if(!filteredNoDisplay.isEmpty()){
|
||||||
// Get the WebElements with the duration information
|
// Get the Webelements with the link and title information
|
||||||
List<WebElement> durationOfEpisodes = new ArrayList<>();
|
List<WebElement> linksToEpisodes = new ArrayList<>();
|
||||||
filteredNoDisplay.forEach(e ->
|
filteredNoDisplay.forEach(e ->
|
||||||
durationOfEpisodes.addAll(e.findElements(By.xpath(".//div/table/tbody/tr/td[3]")))
|
linksToEpisodes.addAll(e.findElements(By.xpath(".//div/table/tbody/tr/td/a")))
|
||||||
);
|
);
|
||||||
// Put the information into episode wrappers and return the list
|
// Get the WebElements with the duration information
|
||||||
List<EpisodeWrapper> wrappers = new ArrayList<>();
|
List<WebElement> durationOfEpisodes = new ArrayList<>();
|
||||||
for(int i=0;i<linksToEpisodes.size();i++){
|
filteredNoDisplay.forEach(e ->
|
||||||
wrappers.add(new EpisodeWrapper(linksToEpisodes.get(i).getAttribute("href"), linksToEpisodes.get(i).getText(), linksToEpisodes.get(i).getText(), seasonName, show, durationOfEpisodes.get(i).getText()));
|
durationOfEpisodes.addAll(e.findElements(By.xpath(".//div/table/tbody/tr/td[3]")))
|
||||||
}
|
);
|
||||||
return wrappers;
|
// Put the information into episode wrappers and return the list
|
||||||
}else{
|
List<EpisodeWrapper> wrappers = new ArrayList<>();
|
||||||
logger.log(Level.WARN, "No episodes found for url:[{0}], season:[{1}]",url, seasonName);
|
for(int i=0;i<linksToEpisodes.size();i++){
|
||||||
return new ArrayList<>();
|
wrappers.add(new EpisodeWrapper(linksToEpisodes.get(i).getAttribute("href"), linksToEpisodes.get(i).getText(), linksToEpisodes.get(i).getText(), seasonName, show, durationOfEpisodes.get(i).getText()));
|
||||||
}
|
}
|
||||||
}else{
|
return wrappers;
|
||||||
logger.log(Level.ERROR, "No seaons found for url:[{0}], season:[{1}]",url, seasonName);
|
}else{
|
||||||
return new ArrayList<>();
|
logger.log(Level.WARN, "No episodes found for url:[{0}], season:[{1}]",url, seasonName);
|
||||||
}
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
}else{
|
||||||
public static String getFetchfileURL(String myspassURL){
|
logger.log(Level.ERROR, "No seaons found for url:[{0}], season:[{1}]",url, seasonName);
|
||||||
synchronized(driverFetchFile){
|
return new ArrayList<>();
|
||||||
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);
|
* Returns the FetchFile.net URL to the myspass.com video file
|
||||||
WebElement downloadButton = wait.until(ExpectedConditions.elementToBeClickable(By.className("download-link")));
|
* @param myspassURL the URL to the myspass.com video
|
||||||
String downloadLink = downloadButton.getAttribute("href");
|
* @return the FetchFile.net URL
|
||||||
driverFetchFile.get("https://de.fetchfile.net/herunterladen-von-myspass");
|
*/
|
||||||
|
public static String getFetchfileURL(String myspassURL){
|
||||||
// Close advertisement tabs
|
synchronized(driverFetchFile){
|
||||||
List<String> tabs = new ArrayList<>(driverFetchFile.getWindowHandles());
|
WebElement videoPathInput = driverFetchFile.findElement(By.id("videoPath"));
|
||||||
for (int i=1;i<tabs.size();i++){
|
videoPathInput.sendKeys(myspassURL);
|
||||||
driverFetchFile.switchTo().window(tabs.get(i));
|
WebElement submitButton = driverFetchFile.findElement(By.id("home-submit"));
|
||||||
driverFetchFile.close();
|
submitButton.click();
|
||||||
}
|
WebDriverWait wait = new WebDriverWait(driverFetchFile, 20);
|
||||||
driverFetchFile.switchTo().window(driverFetchFile.getWindowHandles().toArray(new String[1])[0]);
|
WebElement downloadButton = wait.until(ExpectedConditions.elementToBeClickable(By.className("download-link")));
|
||||||
|
String downloadLink = downloadButton.getAttribute("href");
|
||||||
return downloadLink;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,168 +1,124 @@
|
|||||||
package com.greinet.tvtotalripper.crawler;
|
package com.greinet.tvtotalripper.crawler;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A wrapper class to collect all episode information
|
* A wrapper class to collect all episode information
|
||||||
* @author agreiner
|
* @author agreiner
|
||||||
*/
|
*/
|
||||||
public class EpisodeWrapper {
|
public class EpisodeWrapper {
|
||||||
|
|
||||||
/** The URL to the episode */
|
/** The URL to the episode */
|
||||||
private String url;
|
private final String url;
|
||||||
/** The title */
|
/** The title */
|
||||||
private String title;
|
private final String title;
|
||||||
/** The episode */
|
/** The episode */
|
||||||
private String episode;
|
private final String episode;
|
||||||
/** The season */
|
/** The season */
|
||||||
private String season;
|
private final String season;
|
||||||
/** The episode duration */
|
/** The episode duration */
|
||||||
private String duration;
|
private final String duration;
|
||||||
/** The unique id */
|
/** The unique id */
|
||||||
private final long id;
|
private final long id;
|
||||||
|
/** The show */
|
||||||
private String show;
|
private final String show;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a episode wrapper to store episode information
|
* Create a episode wrapper to store episode information
|
||||||
* @param url the URL
|
* @param url the URL
|
||||||
* @param title the title
|
* @param title the title
|
||||||
* @param episodeNumber the episode
|
* @param episode the episode
|
||||||
* @param season the season
|
* @param season the season
|
||||||
* @param show the show
|
* @param show the show
|
||||||
* @param duration the duration
|
* @param duration the duration
|
||||||
*/
|
*/
|
||||||
public EpisodeWrapper(String url, String title, String episode, String season, String show, String duration) {
|
public EpisodeWrapper(String url, String title, String episode, String season, String show, String duration) {
|
||||||
this.url = url;
|
this.url = url;
|
||||||
this.title = title;
|
this.title = title;
|
||||||
this.episode = episode;
|
this.episode = episode;
|
||||||
this.season = season;
|
this.season = season;
|
||||||
this.show = show;
|
this.show = show;
|
||||||
this.duration = duration;
|
this.duration = duration;
|
||||||
id = UUID.randomUUID().getLeastSignificantBits();
|
id = UUID.randomUUID().getLeastSignificantBits();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getShow() {
|
public String getShow() {
|
||||||
return show;
|
return show;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setShow(String show) {
|
/**
|
||||||
this.show = show;
|
* Return the unique id
|
||||||
}
|
* @return the id
|
||||||
|
*/
|
||||||
/**
|
public long getId() {
|
||||||
* Return the unique id
|
return id;
|
||||||
* @return the id
|
}
|
||||||
*/
|
|
||||||
public long getId() {
|
/**
|
||||||
return id;
|
* Get the URL to the episode
|
||||||
}
|
* @return the URL
|
||||||
|
*/
|
||||||
/**
|
public String getUrl() {
|
||||||
* Get the URL to the episode
|
return url;
|
||||||
* @return the URL
|
}
|
||||||
*/
|
|
||||||
public String getUrl() {
|
/**
|
||||||
return url;
|
* Get the title of the episode
|
||||||
}
|
* @return the title
|
||||||
|
*/
|
||||||
/**
|
public String getTitle() {
|
||||||
* Set the URL to the episode
|
return title;
|
||||||
* @param url the URL
|
}
|
||||||
*/
|
|
||||||
public void setUrl(String url) {
|
/**
|
||||||
this.url = url;
|
* Get the episode
|
||||||
}
|
* @return the episode
|
||||||
|
*/
|
||||||
/**
|
public String getEpisode() {
|
||||||
* Get the title of the episode
|
return episode;
|
||||||
* @return the title
|
}
|
||||||
*/
|
|
||||||
public String getTitle() {
|
/**
|
||||||
return title;
|
* Get the season of the episode
|
||||||
}
|
* @return the season
|
||||||
|
*/
|
||||||
/**
|
public String getSeason() {
|
||||||
* Set the title of the episode
|
return season;
|
||||||
* @param title the title
|
}
|
||||||
*/
|
|
||||||
public void setTitle(String title) {
|
/**
|
||||||
this.title = title;
|
* Get the duration of the episode
|
||||||
}
|
* @return the duration
|
||||||
|
*/
|
||||||
/**
|
public String getDuration() {
|
||||||
* Get the episode
|
return duration;
|
||||||
* @return the episode
|
}
|
||||||
*/
|
|
||||||
public String getEpisode() {
|
@Override
|
||||||
return episode;
|
public String toString() {
|
||||||
}
|
return title;
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Set the episode number
|
@Override
|
||||||
* @param episode the episode
|
public int hashCode() {
|
||||||
*/
|
int hash = 3;
|
||||||
public void setEpisodeNumber(String episode) {
|
hash = 19 * hash + Objects.hashCode(this.url);
|
||||||
this.episode = episode;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Get the season of the episode
|
public boolean equals(Object obj) {
|
||||||
* @return the season
|
if (this == obj) {
|
||||||
*/
|
return true;
|
||||||
public String getSeason() {
|
}
|
||||||
return season;
|
if (obj == null) {
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
/**
|
if (getClass() != obj.getClass()) {
|
||||||
* Set the season of the episode
|
return false;
|
||||||
* @param season the season
|
}
|
||||||
*/
|
final EpisodeWrapper other = (EpisodeWrapper) obj;
|
||||||
public void setSeason(String season) {
|
return Objects.equals(this.url, other.url);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,81 +1,74 @@
|
|||||||
/*
|
/*
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
* To change this license header, choose License Headers in Project Properties.
|
||||||
* To change this template file, choose Tools | Templates
|
* To change this template file, choose Tools | Templates
|
||||||
* and open the template in the editor.
|
* and open the template in the editor.
|
||||||
*/
|
*/
|
||||||
package com.greinet.tvtotalripper.download;
|
package com.greinet.tvtotalripper.download;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author agreiner
|
* @author agreiner
|
||||||
*/
|
*/
|
||||||
public class ConnectionUtil {
|
public class ConnectionUtil {
|
||||||
|
|
||||||
private HttpURLConnection httpConn;
|
/** The connection to the download URL */
|
||||||
private InputStream inputStream;
|
private HttpURLConnection httpConn;
|
||||||
private String fileName;
|
/** The input stream from the connection */
|
||||||
private int contentLength;
|
private InputStream inputStream;
|
||||||
|
/** The download size */
|
||||||
public void prepare(String fileUrl) throws IOException{
|
private int contentLength;
|
||||||
URL url = new URL(fileUrl);
|
|
||||||
httpConn = (HttpURLConnection) url.openConnection();
|
/**
|
||||||
int responseCode = httpConn.getResponseCode();
|
* Prepare the download connection and open the input stream
|
||||||
|
* @param fileUrl the URL to the file
|
||||||
// always check HTTP response code first
|
* @throws IOException error preparing download
|
||||||
if (responseCode == HttpURLConnection.HTTP_OK) {
|
*/
|
||||||
String disposition = httpConn.getHeaderField("Content-Disposition");
|
public void prepare(String fileUrl) throws IOException{
|
||||||
String contentType = httpConn.getContentType();
|
URL url = new URL(fileUrl);
|
||||||
contentLength = httpConn.getContentLength();
|
httpConn = (HttpURLConnection) url.openConnection();
|
||||||
|
int responseCode = httpConn.getResponseCode();
|
||||||
if (disposition != null) {
|
|
||||||
// extracts file name from header field
|
// always check HTTP response code first
|
||||||
int index = disposition.indexOf("filename=");
|
if (responseCode == HttpURLConnection.HTTP_OK) {
|
||||||
if (index > 0) {
|
contentLength = httpConn.getContentLength();
|
||||||
fileName = disposition.substring(index + 10,
|
// opens input stream from the HTTP connection
|
||||||
disposition.length() - 1);
|
inputStream = httpConn.getInputStream();
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// extracts file name from URL
|
throw new IOException(
|
||||||
fileName = fileUrl.substring(fileUrl.lastIndexOf("/") + 1,
|
"No file to download. Server replied HTTP code: "
|
||||||
fileUrl.length());
|
+ responseCode);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// output for debugging purpose only
|
|
||||||
System.out.println("Content-Type = " + contentType);
|
/**
|
||||||
System.out.println("Content-Disposition = " + disposition);
|
* Close the inout stream and disconnect the download
|
||||||
System.out.println("Content-Length = " + contentLength);
|
* @throws IOException
|
||||||
System.out.println("fileName = " + fileName);
|
*/
|
||||||
|
public void disconnect() throws IOException {
|
||||||
// opens input stream from the HTTP connection
|
inputStream.close();
|
||||||
inputStream = httpConn.getInputStream();
|
httpConn.disconnect();
|
||||||
|
}
|
||||||
} else {
|
|
||||||
throw new IOException(
|
/**
|
||||||
"No file to download. Server replied HTTP code: "
|
* Get the content length of the download
|
||||||
+ responseCode);
|
* @return the content length
|
||||||
}
|
*/
|
||||||
}
|
public int getContentLength() {
|
||||||
|
return this.contentLength;
|
||||||
public void disconnect() throws IOException {
|
}
|
||||||
inputStream.close();
|
|
||||||
httpConn.disconnect();
|
/**
|
||||||
}
|
* Get the download related inout stream
|
||||||
|
* @return the input stream
|
||||||
public String getFileName() {
|
*/
|
||||||
return this.fileName;
|
public InputStream getInputStream() {
|
||||||
}
|
return this.inputStream;
|
||||||
|
}
|
||||||
public int getContentLength() {
|
|
||||||
return this.contentLength;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public InputStream getInputStream() {
|
|
||||||
return this.inputStream;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
@ -1,95 +1,130 @@
|
|||||||
package com.greinet.tvtotalripper.download;
|
package com.greinet.tvtotalripper.download;
|
||||||
|
|
||||||
import com.greinet.tvtotalripper.MetaDataWriter;
|
import com.greinet.tvtotalripper.crawler.CrawlerUtil;
|
||||||
import com.greinet.tvtotalripper.crawler.CrawlerUtil;
|
import com.greinet.tvtotalripper.crawler.EpisodeWrapper;
|
||||||
import com.greinet.tvtotalripper.crawler.EpisodeWrapper;
|
import com.greinet.tvtotalripper.ui.SettingsRipperPanel;
|
||||||
import com.greinet.tvtotalripper.ui.SettingsRipperPanel;
|
import java.io.File;
|
||||||
import java.io.File;
|
import java.io.FileOutputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.IOException;
|
||||||
import java.io.IOException;
|
import java.io.InputStream;
|
||||||
import java.io.InputStream;
|
import javax.swing.SwingWorker;
|
||||||
import javax.swing.SwingWorker;
|
|
||||||
|
/**
|
||||||
/**
|
* A task representing a single file download
|
||||||
*
|
*/
|
||||||
* @author agreiner
|
public class DownloadTask extends SwingWorker<Void, Void> {
|
||||||
*/
|
|
||||||
public class DownloadTask extends SwingWorker<Void, Void> {
|
/** The download buffer size */
|
||||||
private static final int BUFFER_SIZE = 4096;
|
private static final int BUFFER_SIZE = 4096;
|
||||||
|
/** The episode of the downloaded file */
|
||||||
private final EpisodeWrapper episodeWrapper;
|
private final EpisodeWrapper episodeWrapper;
|
||||||
|
|
||||||
|
/**
|
||||||
public DownloadTask(EpisodeWrapper episodeWrapper) {
|
* Create a download task for a episode
|
||||||
this.episodeWrapper = episodeWrapper;
|
* @param episodeWrapper
|
||||||
}
|
*/
|
||||||
|
public DownloadTask(EpisodeWrapper episodeWrapper) {
|
||||||
public EpisodeWrapper getEpisodeWrapper() {
|
this.episodeWrapper = episodeWrapper;
|
||||||
return episodeWrapper;
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
@Override
|
* Get the episode of the download
|
||||||
public String toString() {
|
* @return the episode
|
||||||
return episodeWrapper.getTitle();
|
*/
|
||||||
}
|
public EpisodeWrapper getEpisodeWrapper() {
|
||||||
|
return episodeWrapper;
|
||||||
public long fileSize = 0;
|
}
|
||||||
|
|
||||||
public long totalBytesRead = 0;
|
@Override
|
||||||
|
public String toString() {
|
||||||
public int percentCompleted = 0;
|
return episodeWrapper.getTitle();
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Executed in background thread
|
/** The bytes size of the file to download */
|
||||||
*/
|
private long fileSize = 0;
|
||||||
@Override
|
|
||||||
protected Void doInBackground() throws Exception {
|
/** the amount of downloaded bytes */
|
||||||
try {
|
private long totalBytesRead = 0;
|
||||||
String downloadURL = CrawlerUtil.getFetchfileURL(episodeWrapper.getUrl());
|
|
||||||
|
/** The completion percentage */
|
||||||
ConnectionUtil util = new ConnectionUtil();
|
private int percentCompleted = 0;
|
||||||
util.prepare(downloadURL);
|
|
||||||
|
/**
|
||||||
InputStream inputStream = util.getInputStream();
|
* Executed in background thread
|
||||||
|
*/
|
||||||
String fixedTitle = episodeWrapper.getTitle().replace(":", " ");
|
@Override
|
||||||
|
protected Void doInBackground() throws Exception {
|
||||||
File outputFile = new File(SettingsRipperPanel.DOWNLOADFOLDER, fixedTitle+".mp4");
|
try {
|
||||||
FileOutputStream outputStream = new FileOutputStream(outputFile);
|
String downloadURL = CrawlerUtil.getFetchfileURL(episodeWrapper.getUrl());
|
||||||
byte[] buffer = new byte[BUFFER_SIZE];
|
|
||||||
int bytesRead = -1;
|
ConnectionUtil util = new ConnectionUtil();
|
||||||
totalBytesRead = 0;
|
util.prepare(downloadURL);
|
||||||
percentCompleted = 0;
|
|
||||||
int oldPercentCompleted = 0;
|
InputStream inputStream = util.getInputStream();
|
||||||
fileSize = util.getContentLength();
|
|
||||||
|
String fixedTitle = episodeWrapper.getTitle().replace(":", " ");
|
||||||
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
|
||||||
outputStream.write(buffer, 0, bytesRead);
|
File outputFile = new File(SettingsRipperPanel.DOWNLOADFOLDER, fixedTitle+".mp4");
|
||||||
totalBytesRead += bytesRead;
|
try (FileOutputStream outputStream = new FileOutputStream(outputFile)) {
|
||||||
percentCompleted = (int) (totalBytesRead * 100 / fileSize);
|
byte[] buffer = new byte[BUFFER_SIZE];
|
||||||
firePropertyChange(Long.toString(episodeWrapper.getId()), oldPercentCompleted, percentCompleted);
|
int bytesRead;
|
||||||
oldPercentCompleted = percentCompleted;
|
totalBytesRead = 0;
|
||||||
//setProgress(percentCompleted);
|
percentCompleted = 0;
|
||||||
}
|
int oldPercentCompleted = 0;
|
||||||
|
fileSize = util.getContentLength();
|
||||||
outputStream.close();
|
|
||||||
|
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||||
util.disconnect();
|
outputStream.write(buffer, 0, bytesRead);
|
||||||
|
totalBytesRead += bytesRead;
|
||||||
MetaDataWriter mdp = new MetaDataWriter();
|
percentCompleted = (int) (totalBytesRead * 100 / fileSize);
|
||||||
mdp.writeMetadata(outputFile.getAbsolutePath(), episodeWrapper.getTitle(), episodeWrapper.getShow(), episodeWrapper.getShow(), episodeWrapper.getEpisode());
|
firePropertyChange(Long.toString(episodeWrapper.getId()), oldPercentCompleted, percentCompleted);
|
||||||
|
oldPercentCompleted = percentCompleted;
|
||||||
} catch (IOException ex) {
|
//setProgress(percentCompleted);
|
||||||
cancel(true);
|
}
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
util.disconnect();
|
||||||
|
|
||||||
/**
|
|
||||||
* Executed in Swing's event dispatching thread
|
// Enable MetaDataWriter
|
||||||
*/
|
// MetaDataWriter mdp = new MetaDataWriter();
|
||||||
@Override
|
// mdp.writeMetadata(outputFile.getAbsolutePath(), episodeWrapper.getTitle(), episodeWrapper.getShow(), episodeWrapper.getShow(), episodeWrapper.getEpisode());
|
||||||
protected void done() {
|
|
||||||
firePropertyChange(Long.toString(episodeWrapper.getId()), 0, 101);
|
} catch (IOException ex) {
|
||||||
}
|
cancel(true);
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the download file size in bytes
|
||||||
|
* @return the file size in bytes
|
||||||
|
*/
|
||||||
|
public long getFileSize() {
|
||||||
|
return fileSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the amount of already downloaded bytes
|
||||||
|
* @return the amount of already downloaded bytes
|
||||||
|
*/
|
||||||
|
public long getTotalBytesRead() {
|
||||||
|
return totalBytesRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the completiton percentage
|
||||||
|
* @return the completition percentage
|
||||||
|
*/
|
||||||
|
public int getPercentCompleted() {
|
||||||
|
return percentCompleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fire a property change event with the progress value of 101 meaning that the task is done
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void done() {
|
||||||
|
firePropertyChange(Long.toString(episodeWrapper.getId()), 0, 101);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,116 +1,141 @@
|
|||||||
package com.greinet.tvtotalripper.ui;
|
package com.greinet.tvtotalripper.ui;
|
||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.GridBagConstraints;
|
import java.awt.GridBagConstraints;
|
||||||
import java.awt.GridBagLayout;
|
import java.awt.GridBagLayout;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import javax.swing.BorderFactory;
|
import javax.swing.BorderFactory;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
import javax.swing.JList;
|
import javax.swing.JList;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import javax.swing.JScrollPane;
|
import javax.swing.JScrollPane;
|
||||||
import javax.swing.JTextField;
|
import javax.swing.JTextField;
|
||||||
import javax.swing.event.ListSelectionEvent;
|
import javax.swing.event.ListSelectionEvent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* The base panel for show and episode visualization
|
||||||
* @author agreiner
|
*/
|
||||||
*/
|
public class BaseRipperPanel {
|
||||||
public class BaseRipperPanel {
|
|
||||||
|
/** The JPanel */
|
||||||
private final JPanel panel;
|
private final JPanel panel;
|
||||||
private JList<String> listPanel;
|
/** The Jlist for the elements */
|
||||||
private JTextField textField;
|
private JList<String> listPanel;
|
||||||
private final JButton button;
|
/** The text field for the currently selected element */
|
||||||
private final JLabel label;
|
private JTextField textField;
|
||||||
private Map<String, String> elements;
|
/** The button to select the currently selected element */
|
||||||
|
private final JButton button;
|
||||||
public BaseRipperPanel(Map<String, String> elements){
|
/** The label for the element count */
|
||||||
this.elements = elements;
|
private final JLabel label;
|
||||||
|
/** The map for the elements */
|
||||||
if(elements.isEmpty()){
|
private final Map<String, String> elements;
|
||||||
elements.put("Dummy-Key", "Dummy-URL");
|
|
||||||
}
|
/**
|
||||||
|
* Create a base panel the display elements and select a specific one
|
||||||
panel = new JPanel(false);
|
* @param elements the elements
|
||||||
panel.setLayout(new GridBagLayout());
|
*/
|
||||||
|
public BaseRipperPanel(Map<String, String> elements){
|
||||||
GridBagConstraints c = new GridBagConstraints();
|
this.elements = elements;
|
||||||
c.gridx = 0;
|
|
||||||
c.gridy = 0;
|
if(elements.isEmpty()){
|
||||||
c.gridwidth = 3;
|
elements.put("Dummy-Key", "Dummy-URL");
|
||||||
c.fill = GridBagConstraints.BOTH;
|
}
|
||||||
c.weightx = 0.75;
|
|
||||||
|
panel = new JPanel(false);
|
||||||
textField = new JTextField();
|
panel.setLayout(new GridBagLayout());
|
||||||
textField.setEditable(false);
|
|
||||||
panel.add(textField,c);
|
GridBagConstraints c = new GridBagConstraints();
|
||||||
|
c.gridx = 0;
|
||||||
button = new JButton("Select");
|
c.gridy = 0;
|
||||||
c.gridx = 3;
|
c.gridwidth = 3;
|
||||||
c.gridwidth = 2;
|
c.fill = GridBagConstraints.BOTH;
|
||||||
c.weightx = 0.25;
|
c.weightx = 0.75;
|
||||||
|
|
||||||
panel.add(button,c);
|
textField = new JTextField();
|
||||||
|
textField.setEditable(false);
|
||||||
|
panel.add(textField,c);
|
||||||
List<String> names = new ArrayList<>(elements.keySet());
|
|
||||||
Collections.sort(names);
|
button = new JButton("Select");
|
||||||
|
c.gridx = 3;
|
||||||
listPanel = new JList<>(names.toArray(new String[elements.keySet().size()]));
|
c.gridwidth = 2;
|
||||||
JScrollPane scrollPane = new JScrollPane();
|
c.weightx = 0.25;
|
||||||
scrollPane.setViewportView(listPanel);
|
|
||||||
listPanel.setLayoutOrientation(JList.VERTICAL);
|
panel.add(button,c);
|
||||||
listPanel.setSelectedIndex(0);
|
|
||||||
|
|
||||||
listPanel.addListSelectionListener((ListSelectionEvent e) -> {
|
List<String> names = new ArrayList<>(elements.keySet());
|
||||||
textField.setText(listPanel.getSelectedValue());
|
Collections.sort(names);
|
||||||
});
|
|
||||||
textField.setText(listPanel.getSelectedValue());
|
listPanel = new JList<>(names.toArray(new String[elements.keySet().size()]));
|
||||||
|
JScrollPane scrollPane = new JScrollPane();
|
||||||
c.gridx = 0;
|
scrollPane.setViewportView(listPanel);
|
||||||
c.gridy = 1;
|
listPanel.setLayoutOrientation(JList.VERTICAL);
|
||||||
c.gridwidth = 5;
|
listPanel.setSelectedIndex(0);
|
||||||
c.gridheight = 5;
|
|
||||||
c.weightx = 0;
|
listPanel.addListSelectionListener((ListSelectionEvent e) -> {
|
||||||
|
textField.setText(listPanel.getSelectedValue());
|
||||||
panel.add(scrollPane,c);
|
});
|
||||||
|
textField.setText(listPanel.getSelectedValue());
|
||||||
label = new JLabel(Integer.toString(elements.keySet().size()));
|
|
||||||
label.setBorder(BorderFactory.createLineBorder(Color.BLACK, 1));
|
c.gridx = 0;
|
||||||
|
c.gridy = 1;
|
||||||
|
c.gridwidth = 5;
|
||||||
c.gridx = 4;
|
c.gridheight = 5;
|
||||||
c.gridy = 6;
|
c.weightx = 0;
|
||||||
c.gridwidth = 1;
|
|
||||||
c.gridheight = 1;
|
panel.add(scrollPane,c);
|
||||||
c.fill = GridBagConstraints.NONE;
|
|
||||||
c.anchor = GridBagConstraints.EAST;
|
label = new JLabel(Integer.toString(elements.keySet().size()));
|
||||||
|
label.setBorder(BorderFactory.createLineBorder(Color.BLACK, 1));
|
||||||
panel.add(label, c);
|
|
||||||
|
|
||||||
|
c.gridx = 4;
|
||||||
|
c.gridy = 6;
|
||||||
}
|
c.gridwidth = 1;
|
||||||
|
c.gridheight = 1;
|
||||||
public JPanel getJPanel(){
|
c.fill = GridBagConstraints.NONE;
|
||||||
return panel;
|
c.anchor = GridBagConstraints.EAST;
|
||||||
}
|
|
||||||
|
panel.add(label, c);
|
||||||
public JButton getJButton(){
|
|
||||||
return button;
|
|
||||||
}
|
|
||||||
|
}
|
||||||
public String getCurrentSelected(){
|
|
||||||
return elements.get(listPanel.getSelectedValue());
|
/**
|
||||||
}
|
* Get the JPanel
|
||||||
|
* @return the JPanel
|
||||||
public JList getJList(){
|
*/
|
||||||
return listPanel;
|
public JPanel getJPanel(){
|
||||||
}
|
return panel;
|
||||||
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
|
* Get the selection JButton
|
||||||
|
* @return the JButton
|
||||||
|
*/
|
||||||
|
public JButton getJButton(){
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the String representing the currently selected element
|
||||||
|
* @return the selected element
|
||||||
|
*/
|
||||||
|
public String getCurrentSelected(){
|
||||||
|
return elements.get(listPanel.getSelectedValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Jlist of the elements
|
||||||
|
* @return the JList
|
||||||
|
*/
|
||||||
|
public JList getJList(){
|
||||||
|
return listPanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -1,143 +1,164 @@
|
|||||||
/*
|
package com.greinet.tvtotalripper.ui;
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
|
||||||
* To change this template file, choose Tools | Templates
|
import com.greinet.tvtotalripper.download.DownloadTask;
|
||||||
* and open the template in the editor.
|
import java.awt.Color;
|
||||||
*/
|
import java.awt.GridBagConstraints;
|
||||||
package com.greinet.tvtotalripper.ui;
|
import java.awt.GridBagLayout;
|
||||||
|
import java.beans.PropertyChangeEvent;
|
||||||
import com.greinet.tvtotalripper.download.DownloadTask;
|
import java.beans.PropertyChangeListener;
|
||||||
import java.awt.Color;
|
import java.util.ArrayList;
|
||||||
import java.awt.GridBagConstraints;
|
import java.util.List;
|
||||||
import java.awt.GridBagLayout;
|
import java.util.Optional;
|
||||||
import java.beans.PropertyChangeEvent;
|
import javax.swing.BorderFactory;
|
||||||
import java.beans.PropertyChangeListener;
|
import javax.swing.DefaultListModel;
|
||||||
import java.util.ArrayList;
|
import javax.swing.JLabel;
|
||||||
import java.util.List;
|
import javax.swing.JList;
|
||||||
import java.util.Optional;
|
import javax.swing.JPanel;
|
||||||
import javax.swing.BorderFactory;
|
import javax.swing.JScrollPane;
|
||||||
import javax.swing.DefaultListModel;
|
import javax.swing.JTextField;
|
||||||
import javax.swing.JLabel;
|
import javax.swing.ListSelectionModel;
|
||||||
import javax.swing.JList;
|
import javax.swing.event.ListSelectionEvent;
|
||||||
import javax.swing.JPanel;
|
|
||||||
import javax.swing.JScrollPane;
|
/**
|
||||||
import javax.swing.JTextField;
|
* A download panel to show the current downloads
|
||||||
import javax.swing.ListSelectionModel;
|
*/
|
||||||
import javax.swing.event.ListSelectionEvent;
|
public class DownloadRipperPanel implements PropertyChangeListener{
|
||||||
|
|
||||||
/**
|
/** The JPanel for download task visualization */
|
||||||
*
|
private final JPanel panel;
|
||||||
* @author agreiner
|
/** The JList for the downloads */
|
||||||
*/
|
private JList<DownloadTask> listTasks;
|
||||||
public class DownloadRipperPanel implements PropertyChangeListener{
|
/** The ListModel for the DownloadTasks */
|
||||||
|
private final DefaultListModel<DownloadTask> listModel;
|
||||||
private final JPanel panel;
|
/** The text field for the currently selected task */
|
||||||
private JList<DownloadTask> listTasks;
|
private JTextField textSelectedTask;
|
||||||
private DefaultListModel<DownloadTask> listModel;
|
/** The JLabel for the DownloadTask count */
|
||||||
private JTextField textSelectedTask;
|
private final JLabel labelTaskCount;
|
||||||
private final JLabel labelTaskCount;
|
/** The list of download tasks */
|
||||||
|
private final List<DownloadTask> downloadTasks;
|
||||||
private final List<DownloadTask> downloadTasks;
|
/** The information panel for the currently selected task */
|
||||||
|
private DownloadTaskInformationPanel infoPanel;
|
||||||
private DownloadTaskInformationPanel infoPanel;
|
|
||||||
|
/**
|
||||||
public DownloadRipperPanel(){
|
* Create a download panel
|
||||||
|
*/
|
||||||
downloadTasks = new ArrayList<>();
|
public DownloadRipperPanel(){
|
||||||
|
|
||||||
panel = new JPanel();
|
downloadTasks = new ArrayList<>();
|
||||||
panel.setLayout(new GridBagLayout());
|
|
||||||
|
panel = new JPanel();
|
||||||
GridBagConstraints c = new GridBagConstraints();
|
panel.setLayout(new GridBagLayout());
|
||||||
|
|
||||||
c.gridx = 0;
|
GridBagConstraints c = new GridBagConstraints();
|
||||||
c.gridy = 0;
|
|
||||||
c.gridwidth = 5;
|
c.gridx = 0;
|
||||||
c.fill = GridBagConstraints.BOTH;
|
c.gridy = 0;
|
||||||
c.weightx = 0.5;
|
c.gridwidth = 5;
|
||||||
textSelectedTask = new JTextField();
|
c.fill = GridBagConstraints.BOTH;
|
||||||
textSelectedTask.setEditable(false);
|
c.weightx = 0.5;
|
||||||
panel.add(textSelectedTask, c);
|
textSelectedTask = new JTextField();
|
||||||
|
textSelectedTask.setEditable(false);
|
||||||
|
panel.add(textSelectedTask, c);
|
||||||
listModel = new DefaultListModel<>();
|
|
||||||
listTasks = new JList<>(listModel);
|
|
||||||
listTasks.setSelectedIndex(ListSelectionModel.SINGLE_SELECTION);
|
listModel = new DefaultListModel<>();
|
||||||
JScrollPane scrollPane = new JScrollPane();
|
listTasks = new JList<>(listModel);
|
||||||
scrollPane.setViewportView(listTasks);
|
listTasks.setSelectedIndex(ListSelectionModel.SINGLE_SELECTION);
|
||||||
listTasks.setLayoutOrientation(JList.VERTICAL);
|
JScrollPane scrollPane = new JScrollPane();
|
||||||
|
scrollPane.setViewportView(listTasks);
|
||||||
c.gridx = 0;
|
listTasks.setLayoutOrientation(JList.VERTICAL);
|
||||||
c.gridy = 1;
|
|
||||||
c.gridwidth = 5;
|
c.gridx = 0;
|
||||||
c.gridheight = 5;
|
c.gridy = 1;
|
||||||
c.weightx = 0.5;
|
c.gridwidth = 5;
|
||||||
panel.add(scrollPane, c);
|
c.gridheight = 5;
|
||||||
|
c.weightx = 0.5;
|
||||||
infoPanel = new DownloadTaskInformationPanel();
|
panel.add(scrollPane, c);
|
||||||
c.gridx = 5;
|
|
||||||
c.gridy = 0;
|
infoPanel = new DownloadTaskInformationPanel();
|
||||||
c.gridwidth = 5;
|
c.gridx = 5;
|
||||||
c.weightx = 0.5;
|
c.gridy = 0;
|
||||||
c.fill = GridBagConstraints.BOTH;
|
c.gridwidth = 5;
|
||||||
panel.add(infoPanel, c);
|
c.weightx = 0.5;
|
||||||
|
c.fill = GridBagConstraints.BOTH;
|
||||||
listTasks.addListSelectionListener((ListSelectionEvent e) -> {
|
panel.add(infoPanel, c);
|
||||||
if(listTasks.getSelectedValue()!=null){
|
|
||||||
textSelectedTask.setText(listTasks.getSelectedValue().toString());
|
listTasks.addListSelectionListener((ListSelectionEvent e) -> {
|
||||||
infoPanel.setInformation(listTasks.getSelectedValue());
|
if(listTasks.getSelectedValue()!=null){
|
||||||
infoPanel.selectedDownloadTask = listTasks.getSelectedValue();
|
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));
|
|
||||||
|
labelTaskCount = new JLabel(Integer.toString(listTasks.getModel().getSize()));
|
||||||
c.gridx = 4;
|
labelTaskCount.setBorder(BorderFactory.createLineBorder(Color.BLACK, 1));
|
||||||
c.gridy = 6;
|
|
||||||
c.gridwidth = 1;
|
c.gridx = 4;
|
||||||
c.gridheight = 1;
|
c.gridy = 6;
|
||||||
c.fill = GridBagConstraints.NONE;
|
c.gridwidth = 1;
|
||||||
c.anchor = GridBagConstraints.EAST;
|
c.gridheight = 1;
|
||||||
panel.add(labelTaskCount, c);
|
c.fill = GridBagConstraints.NONE;
|
||||||
}
|
c.anchor = GridBagConstraints.EAST;
|
||||||
|
panel.add(labelTaskCount, c);
|
||||||
public JPanel getJPanel(){
|
}
|
||||||
return panel;
|
|
||||||
}
|
/**
|
||||||
|
* Get the JPanel
|
||||||
public DownloadTask getSelectedTask(){
|
* @return the JPanel
|
||||||
return listTasks.getSelectedValue();
|
*/
|
||||||
}
|
public JPanel getJPanel(){
|
||||||
|
return panel;
|
||||||
public void addTask(DownloadTask task){
|
}
|
||||||
downloadTasks.add(task);
|
|
||||||
task.execute();
|
/**
|
||||||
listModel.addElement(task);
|
* Get the currently selected task
|
||||||
updateTaskCount();
|
* @return the selected task
|
||||||
task.addPropertyChangeListener(this);
|
*/
|
||||||
}
|
public DownloadTask getSelectedTask(){
|
||||||
|
return listTasks.getSelectedValue();
|
||||||
|
}
|
||||||
@Override
|
|
||||||
public void propertyChange(PropertyChangeEvent evt) {
|
/**
|
||||||
if(evt.getNewValue().equals(101)){
|
* Add a download task to be executed
|
||||||
Optional<DownloadTask> taskToRemove = downloadTasks.stream().filter(t -> Long.toString(t.getEpisodeWrapper().getId()).equals(evt.getPropertyName())).findFirst();
|
* @param task
|
||||||
if(taskToRemove.isPresent()){
|
*/
|
||||||
if(listTasks.getSelectedValue() != null && taskToRemove.get().equals(listTasks.getSelectedValue())){
|
public void addTask(DownloadTask task){
|
||||||
infoPanel.clearInformation();
|
downloadTasks.add(task);
|
||||||
textSelectedTask.setText("");
|
task.execute();
|
||||||
}
|
listModel.addElement(task);
|
||||||
downloadTasks.remove(taskToRemove.get());
|
updateTaskCount();
|
||||||
listModel.removeElement(taskToRemove.get());
|
task.addPropertyChangeListener(this);
|
||||||
updateTaskCount();
|
}
|
||||||
|
|
||||||
}
|
|
||||||
}
|
/**
|
||||||
panel.repaint();
|
* Listen to the property change events and remove a task if the new value is 101
|
||||||
}
|
* @param evt the event parameters
|
||||||
|
*/
|
||||||
private void updateTaskCount(){
|
@Override
|
||||||
labelTaskCount.setText(Integer.toString(listTasks.getModel().getSize()));
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the current task count
|
||||||
|
*/
|
||||||
|
private void updateTaskCount(){
|
||||||
|
labelTaskCount.setText(Integer.toString(listTasks.getModel().getSize()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,126 +1,147 @@
|
|||||||
/*
|
/*
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
* To change this license header, choose License Headers in Project Properties.
|
||||||
* To change this template file, choose Tools | Templates
|
* To change this template file, choose Tools | Templates
|
||||||
* and open the template in the editor.
|
* and open the template in the editor.
|
||||||
*/
|
*/
|
||||||
package com.greinet.tvtotalripper.ui;
|
package com.greinet.tvtotalripper.ui;
|
||||||
|
|
||||||
import com.greinet.tvtotalripper.download.DownloadTask;
|
import com.greinet.tvtotalripper.download.DownloadTask;
|
||||||
import java.awt.GridLayout;
|
import java.awt.GridLayout;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.beans.PropertyChangeListener;
|
import javax.swing.JLabel;
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JPanel;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JProgressBar;
|
||||||
import javax.swing.JProgressBar;
|
import javax.swing.JTextField;
|
||||||
import javax.swing.JTextField;
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.apache.commons.io.FileUtils;
|
|
||||||
|
/**
|
||||||
/**
|
*
|
||||||
*
|
* @author agreiner
|
||||||
* @author agreiner
|
*/
|
||||||
*/
|
public class DownloadTaskInformationPanel extends JPanel{
|
||||||
public class DownloadTaskInformationPanel extends JPanel{
|
|
||||||
|
/** The label for the title */
|
||||||
private JLabel labelTitle;
|
private final JLabel labelTitle;
|
||||||
private JLabel labelShow;
|
/** The label for the show */
|
||||||
private JLabel labelSeason;
|
private final JLabel labelShow;
|
||||||
private JLabel labelEpisode;
|
/** The label for the season */
|
||||||
private JLabel labelDuration;
|
private final JLabel labelSeason;
|
||||||
private JLabel labelProgress;
|
/** The label for the episode */
|
||||||
|
private final JLabel labelEpisode;
|
||||||
private JTextField textTitle;
|
/** The label for the duration */
|
||||||
private JTextField textShow;
|
private final JLabel labelDuration;
|
||||||
private JTextField textSeason;
|
/** The label for the download progress */
|
||||||
private JTextField textEpisode;
|
private final JLabel labelProgress;
|
||||||
private JTextField textDuration;
|
/** The text field for the title */
|
||||||
private JProgressBar progressBar;
|
private final JTextField textTitle;
|
||||||
|
/** The text field for the show */
|
||||||
public DownloadTask selectedDownloadTask;
|
private final JTextField textShow;
|
||||||
|
/** The text field for the season */
|
||||||
public DownloadTaskInformationPanel(){
|
private final JTextField textSeason;
|
||||||
|
/** The text field for the episode */
|
||||||
labelTitle = new JLabel("Title");
|
private final JTextField textEpisode;
|
||||||
labelShow = new JLabel("Show");
|
/** The text field for the duration */
|
||||||
labelSeason = new JLabel("Season");
|
private final JTextField textDuration;
|
||||||
labelEpisode = new JLabel("Episode");
|
/** The text field for the download progress */
|
||||||
labelDuration = new JLabel("Duration");
|
private final JProgressBar progressBar;
|
||||||
labelProgress = new JLabel("Progress");
|
|
||||||
|
/** The currently visualized download task */
|
||||||
textTitle = new JTextField();
|
public DownloadTask selectedDownloadTask;
|
||||||
textTitle.setEditable(false);
|
|
||||||
|
/**
|
||||||
textShow = new JTextField();
|
* Create a Panel for DownloadTask information visualization
|
||||||
textShow.setEditable(false);
|
*/
|
||||||
|
public DownloadTaskInformationPanel(){
|
||||||
textSeason = new JTextField();
|
|
||||||
textSeason.setEditable(false);
|
labelTitle = new JLabel("Title");
|
||||||
|
labelShow = new JLabel("Show");
|
||||||
textEpisode = new JTextField();
|
labelSeason = new JLabel("Season");
|
||||||
textEpisode.setEditable(false);
|
labelEpisode = new JLabel("Episode");
|
||||||
|
labelDuration = new JLabel("Duration");
|
||||||
textDuration = new JTextField();
|
labelProgress = new JLabel("Progress");
|
||||||
textDuration.setEditable(false);
|
|
||||||
|
textTitle = new JTextField();
|
||||||
progressBar = new JProgressBar(0, 100);
|
textTitle.setEditable(false);
|
||||||
|
|
||||||
setLayout(new GridLayout(6, 2));
|
textShow = new JTextField();
|
||||||
|
textShow.setEditable(false);
|
||||||
add(labelTitle);
|
|
||||||
add(textTitle);
|
textSeason = new JTextField();
|
||||||
add(labelShow);
|
textSeason.setEditable(false);
|
||||||
add(textShow);
|
|
||||||
add(labelSeason);
|
textEpisode = new JTextField();
|
||||||
add(textSeason);
|
textEpisode.setEditable(false);
|
||||||
add(labelEpisode);
|
|
||||||
add(textEpisode);
|
textDuration = new JTextField();
|
||||||
add(labelDuration);
|
textDuration.setEditable(false);
|
||||||
add(textDuration);
|
|
||||||
add(labelProgress);
|
progressBar = new JProgressBar(0, 100);
|
||||||
add(progressBar);
|
|
||||||
}
|
setLayout(new GridLayout(6, 2));
|
||||||
|
|
||||||
public void clearInformation(){
|
add(labelTitle);
|
||||||
this.selectedDownloadTask = null;
|
add(textTitle);
|
||||||
textTitle.setText("");
|
add(labelShow);
|
||||||
textShow.setText("");
|
add(textShow);
|
||||||
textSeason.setText("");
|
add(labelSeason);
|
||||||
textEpisode.setText("");
|
add(textSeason);
|
||||||
textDuration.setText("");
|
add(labelEpisode);
|
||||||
progressBar.setValue(0);
|
add(textEpisode);
|
||||||
progressBar.setString("");
|
add(labelDuration);
|
||||||
}
|
add(textDuration);
|
||||||
|
add(labelProgress);
|
||||||
public void setInformation(DownloadTask downloadTask){
|
add(progressBar);
|
||||||
textTitle.setText(downloadTask.getEpisodeWrapper().getTitle());
|
}
|
||||||
textTitle.setEditable(false);
|
|
||||||
textShow.setText(downloadTask.getEpisodeWrapper().getShow());
|
/**
|
||||||
textShow.setEditable(false);
|
* Clear all information
|
||||||
textSeason.setText(downloadTask.getEpisodeWrapper().getSeason());
|
*/
|
||||||
textSeason.setEditable(false);
|
public void clearInformation(){
|
||||||
textEpisode.setText(downloadTask.getEpisodeWrapper().getEpisode());
|
this.selectedDownloadTask = null;
|
||||||
textEpisode.setEditable(false);
|
textTitle.setText("");
|
||||||
textDuration.setText(downloadTask.getEpisodeWrapper().getDuration());
|
textShow.setText("");
|
||||||
|
textSeason.setText("");
|
||||||
if(selectedDownloadTask != null && selectedDownloadTask.equals(downloadTask)){
|
textEpisode.setText("");
|
||||||
progressBar.setValue(downloadTask.percentCompleted);
|
textDuration.setText("");
|
||||||
progressBar.setStringPainted(true);
|
progressBar.setValue(0);
|
||||||
String done = FileUtils.byteCountToDisplaySize(downloadTask.totalBytesRead);
|
progressBar.setString("");
|
||||||
String todo = FileUtils.byteCountToDisplaySize(downloadTask.fileSize);
|
}
|
||||||
progressBar.setString(done+"/"+todo);
|
|
||||||
}
|
/**
|
||||||
|
* Set the information of the download task
|
||||||
downloadTask.addPropertyChangeListener((PropertyChangeEvent evt) -> {
|
* @param downloadTask the download task
|
||||||
|
*/
|
||||||
if(selectedDownloadTask != null && evt.getPropertyName().equals(Long.toString(selectedDownloadTask.getEpisodeWrapper().getId()))){
|
public void setInformation(DownloadTask downloadTask){
|
||||||
progressBar.setValue((int)evt.getNewValue());
|
textTitle.setText(downloadTask.getEpisodeWrapper().getTitle());
|
||||||
progressBar.setStringPainted(true);
|
textTitle.setEditable(false);
|
||||||
String done = FileUtils.byteCountToDisplaySize(downloadTask.totalBytesRead);
|
textShow.setText(downloadTask.getEpisodeWrapper().getShow());
|
||||||
String todo = FileUtils.byteCountToDisplaySize(downloadTask.fileSize);
|
textShow.setEditable(false);
|
||||||
progressBar.setString(done+"/"+todo);
|
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.getPercentCompleted());
|
||||||
|
progressBar.setStringPainted(true);
|
||||||
|
String done = FileUtils.byteCountToDisplaySize(downloadTask.getTotalBytesRead());
|
||||||
|
String todo = FileUtils.byteCountToDisplaySize(downloadTask.getFileSize());
|
||||||
|
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.getTotalBytesRead());
|
||||||
|
String todo = FileUtils.byteCountToDisplaySize(downloadTask.getFileSize());
|
||||||
|
progressBar.setString(done+"/"+todo);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -1,124 +1,146 @@
|
|||||||
package com.greinet.tvtotalripper.ui;
|
package com.greinet.tvtotalripper.ui;
|
||||||
|
|
||||||
import com.greinet.tvtotalripper.crawler.EpisodeWrapper;
|
import com.greinet.tvtotalripper.crawler.EpisodeWrapper;
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.GridBagConstraints;
|
import java.awt.GridBagConstraints;
|
||||||
import java.awt.GridBagLayout;
|
import java.awt.GridBagLayout;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javax.swing.BorderFactory;
|
import javax.swing.BorderFactory;
|
||||||
import javax.swing.DefaultListModel;
|
import javax.swing.DefaultListModel;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
import javax.swing.JList;
|
import javax.swing.JList;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import javax.swing.JScrollPane;
|
import javax.swing.JScrollPane;
|
||||||
import javax.swing.JTextField;
|
import javax.swing.JTextField;
|
||||||
import javax.swing.event.ListSelectionEvent;
|
import javax.swing.event.ListSelectionEvent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* A panel to display the episodes of a season
|
||||||
* @author agreiner
|
*/
|
||||||
*/
|
public class EpisodesRipperPanel {
|
||||||
public class EpisodesRipperPanel {
|
/** The JPanel */
|
||||||
|
private final JPanel panel;
|
||||||
private final JPanel panel;
|
/** The list for the episodes */
|
||||||
|
private JList<EpisodeWrapper> listPanel;
|
||||||
private JList<EpisodeWrapper> listPanel;
|
/** The list model for the episode list */
|
||||||
private DefaultListModel<EpisodeWrapper> listModel;
|
private DefaultListModel<EpisodeWrapper> listModel;
|
||||||
|
/** The text field for the currently selected episode */
|
||||||
private JTextField textField;
|
private JTextField textField;
|
||||||
private final JButton buttonDownload;
|
/** The button to start the episode download */
|
||||||
private final JLabel label;
|
private final JButton buttonDownload;
|
||||||
private final List<EpisodeWrapper> episodes;
|
/** The label for the episode count */
|
||||||
|
private final JLabel label;
|
||||||
private final JButton buttonDownloadAll;
|
/** The list of episodes */
|
||||||
|
private final List<EpisodeWrapper> episodes;
|
||||||
public EpisodesRipperPanel(List<EpisodeWrapper> episodes){
|
/** The button to download all episodes in the list */
|
||||||
this.episodes = episodes;
|
private final JButton buttonDownloadAll;
|
||||||
|
|
||||||
panel = new JPanel(false);
|
/**
|
||||||
panel.setLayout(new GridBagLayout());
|
* Create a panel to display episodes in a list
|
||||||
|
* @param episodes the episodes to display
|
||||||
GridBagConstraints c = new GridBagConstraints();
|
*/
|
||||||
c.gridx = 0;
|
public EpisodesRipperPanel(List<EpisodeWrapper> episodes){
|
||||||
c.gridy = 0;
|
this.episodes = episodes;
|
||||||
c.gridwidth = 3;
|
|
||||||
c.fill = GridBagConstraints.BOTH;
|
panel = new JPanel(false);
|
||||||
c.weightx = 0.4;
|
panel.setLayout(new GridBagLayout());
|
||||||
|
|
||||||
textField = new JTextField();
|
GridBagConstraints c = new GridBagConstraints();
|
||||||
textField.setEditable(false);
|
c.gridx = 0;
|
||||||
panel.add(textField,c);
|
c.gridy = 0;
|
||||||
|
c.gridwidth = 3;
|
||||||
buttonDownload = new JButton("Download");
|
c.fill = GridBagConstraints.BOTH;
|
||||||
c.gridx = 3;
|
c.weightx = 0.4;
|
||||||
c.gridwidth = 2;
|
|
||||||
c.weightx = 0.1;
|
textField = new JTextField();
|
||||||
|
textField.setEditable(false);
|
||||||
panel.add(buttonDownload,c);
|
panel.add(textField,c);
|
||||||
|
|
||||||
listModel = new DefaultListModel<>();
|
buttonDownload = new JButton("Download");
|
||||||
episodes.forEach(e -> listModel.addElement(e));
|
c.gridx = 3;
|
||||||
|
c.gridwidth = 2;
|
||||||
listPanel = new JList<>(listModel);
|
c.weightx = 0.1;
|
||||||
JScrollPane scrollPane = new JScrollPane();
|
|
||||||
scrollPane.setViewportView(listPanel);
|
panel.add(buttonDownload,c);
|
||||||
listPanel.setLayoutOrientation(JList.VERTICAL);
|
|
||||||
listPanel.setSelectedIndex(0);
|
listModel = new DefaultListModel<>();
|
||||||
|
episodes.forEach(e -> listModel.addElement(e));
|
||||||
listPanel.addListSelectionListener((ListSelectionEvent e) -> {
|
|
||||||
textField.setText(listPanel.getSelectedValue().toString());
|
listPanel = new JList<>(listModel);
|
||||||
});
|
JScrollPane scrollPane = new JScrollPane();
|
||||||
textField.setText(listPanel.getSelectedValue().toString());
|
scrollPane.setViewportView(listPanel);
|
||||||
|
listPanel.setLayoutOrientation(JList.VERTICAL);
|
||||||
c.gridx = 0;
|
listPanel.setSelectedIndex(0);
|
||||||
c.gridy = 1;
|
|
||||||
c.gridwidth = 5;
|
listPanel.addListSelectionListener((ListSelectionEvent e) -> {
|
||||||
c.gridheight = 5;
|
textField.setText(listPanel.getSelectedValue().toString());
|
||||||
c.weightx = 0.5;
|
});
|
||||||
|
textField.setText(listPanel.getSelectedValue().toString());
|
||||||
panel.add(scrollPane,c);
|
|
||||||
|
c.gridx = 0;
|
||||||
label = new JLabel(Integer.toString(episodes.size()));
|
c.gridy = 1;
|
||||||
label.setBorder(BorderFactory.createLineBorder(Color.BLACK, 1));
|
c.gridwidth = 5;
|
||||||
|
c.gridheight = 5;
|
||||||
buttonDownloadAll = new JButton("Download all episodes");
|
c.weightx = 0.5;
|
||||||
c.gridx = 6;
|
|
||||||
c.gridy = 0;
|
panel.add(scrollPane,c);
|
||||||
c.gridwidth = 1;
|
|
||||||
c.gridheight = 6;
|
label = new JLabel(Integer.toString(episodes.size()));
|
||||||
c.weightx = 0.5;
|
label.setBorder(BorderFactory.createLineBorder(Color.BLACK, 1));
|
||||||
panel.add(buttonDownloadAll,c);
|
|
||||||
|
buttonDownloadAll = new JButton("Download all episodes");
|
||||||
|
c.gridx = 6;
|
||||||
|
c.gridy = 0;
|
||||||
c.gridx = 4;
|
c.gridwidth = 1;
|
||||||
c.gridy = 6;
|
c.gridheight = 6;
|
||||||
c.gridwidth = 1;
|
c.weightx = 0.5;
|
||||||
c.gridheight = 1;
|
panel.add(buttonDownloadAll,c);
|
||||||
c.fill = GridBagConstraints.NONE;
|
|
||||||
c.anchor = GridBagConstraints.EAST;
|
|
||||||
|
|
||||||
panel.add(label, c);
|
c.gridx = 4;
|
||||||
|
c.gridy = 6;
|
||||||
|
c.gridwidth = 1;
|
||||||
|
c.gridheight = 1;
|
||||||
}
|
c.fill = GridBagConstraints.NONE;
|
||||||
|
c.anchor = GridBagConstraints.EAST;
|
||||||
public JPanel getJPanel(){
|
|
||||||
return panel;
|
panel.add(label, c);
|
||||||
}
|
|
||||||
|
|
||||||
public EpisodeWrapper getCurrentSelected(){
|
|
||||||
return listPanel.getSelectedValue();
|
}
|
||||||
}
|
/**
|
||||||
|
* Get the JPanel
|
||||||
public JButton getDownloadButton(){
|
* @return the JPanel
|
||||||
return buttonDownload;
|
*/
|
||||||
}
|
public JPanel getJPanel(){
|
||||||
|
return panel;
|
||||||
public JButton getDownloadAllButton(){
|
}
|
||||||
return buttonDownloadAll;
|
|
||||||
}
|
/**
|
||||||
|
* Get the currently selecte episode
|
||||||
}
|
* @return
|
||||||
|
*/
|
||||||
|
public EpisodeWrapper getCurrentSelected(){
|
||||||
|
return listPanel.getSelectedValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the download button
|
||||||
|
* @return the download button
|
||||||
|
*/
|
||||||
|
public JButton getDownloadButton(){
|
||||||
|
return buttonDownload;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the download all button
|
||||||
|
* @return the download all button
|
||||||
|
*/
|
||||||
|
public JButton getDownloadAllButton(){
|
||||||
|
return buttonDownloadAll;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -1,132 +1,153 @@
|
|||||||
package com.greinet.tvtotalripper.ui;
|
package com.greinet.tvtotalripper.ui;
|
||||||
|
|
||||||
import com.greinet.tvtotalripper.crawler.CrawlerUtil;
|
import com.greinet.tvtotalripper.crawler.CrawlerUtil;
|
||||||
import com.greinet.tvtotalripper.crawler.EpisodeWrapper;
|
import com.greinet.tvtotalripper.crawler.EpisodeWrapper;
|
||||||
import com.greinet.tvtotalripper.download.DownloadTask;
|
import com.greinet.tvtotalripper.download.DownloadTask;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JFrame;
|
import javax.swing.JFrame;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import javax.swing.JTabbedPane;
|
import javax.swing.JTabbedPane;
|
||||||
import org.openqa.selenium.By;
|
import org.openqa.selenium.By;
|
||||||
import org.openqa.selenium.WebElement;
|
import org.openqa.selenium.WebElement;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author agreiner
|
* @author agreiner
|
||||||
*/
|
*/
|
||||||
public class RipperWindow {
|
public class RipperWindow {
|
||||||
|
/** The pane managing the tabs */
|
||||||
private final JFrame frame;
|
private final JTabbedPane tabbedPane;
|
||||||
private final JTabbedPane tabbedPane;
|
/** The panel for the shows */
|
||||||
|
private JComponent panel1;
|
||||||
private JComponent panel1;
|
/** The panel for the seasons */
|
||||||
private JComponent panel2;
|
private JComponent panel2;
|
||||||
private JComponent panel3;
|
/** The panel for the episodes */
|
||||||
private JComponent panel4;
|
private JComponent panel3;
|
||||||
private JComponent panel5;
|
/** The panel for the downloads */
|
||||||
|
private final JComponent panel4;
|
||||||
private BaseRipperPanel showRipperPanel;
|
/** The panel for the settings */
|
||||||
private SettingsRipperPanel srp;
|
private final JComponent panel5;
|
||||||
private DownloadRipperPanel drp;
|
/** The show panel */
|
||||||
|
private final BaseRipperPanel showRipperPanel;
|
||||||
public RipperWindow(){
|
/** The settings panel */
|
||||||
frame = new JFrame("TV Total Ripper");
|
private final SettingsRipperPanel srp;
|
||||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
/** The downloads panel */
|
||||||
|
private final DownloadRipperPanel drp;
|
||||||
tabbedPane = new JTabbedPane();
|
|
||||||
|
/**
|
||||||
showRipperPanel = createShowsPanel();
|
* Create the main application UI
|
||||||
srp = new SettingsRipperPanel();
|
*/
|
||||||
drp = new DownloadRipperPanel();
|
public RipperWindow(){
|
||||||
|
JFrame frame = new JFrame("TV Total Ripper");
|
||||||
panel1 = showRipperPanel.getJPanel();
|
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||||
panel2 = new JPanel();
|
|
||||||
panel3 = new JPanel();
|
tabbedPane = new JTabbedPane();
|
||||||
panel4 = drp.getJPanel();
|
|
||||||
panel5 = srp.getPanel();
|
showRipperPanel = createShowsPanel();
|
||||||
|
srp = new SettingsRipperPanel();
|
||||||
tabbedPane.addTab("Shows", panel1);
|
drp = new DownloadRipperPanel();
|
||||||
tabbedPane.addTab("Seasons", panel2);
|
|
||||||
tabbedPane.addTab("Episodes", panel3);
|
panel1 = showRipperPanel.getJPanel();
|
||||||
tabbedPane.addTab("Downloads", panel4);
|
panel2 = new JPanel();
|
||||||
tabbedPane.addTab("Settings", panel5);
|
panel3 = new JPanel();
|
||||||
|
panel4 = drp.getJPanel();
|
||||||
tabbedPane.setEnabledAt(1, false);
|
panel5 = srp.getPanel();
|
||||||
tabbedPane.setEnabledAt(2, false);
|
|
||||||
|
tabbedPane.addTab("Shows", panel1);
|
||||||
tabbedPane.setPreferredSize(new Dimension(900, 400));
|
tabbedPane.addTab("Seasons", panel2);
|
||||||
|
tabbedPane.addTab("Episodes", panel3);
|
||||||
frame.add(tabbedPane);
|
tabbedPane.addTab("Downloads", panel4);
|
||||||
|
tabbedPane.addTab("Settings", panel5);
|
||||||
frame.pack();
|
|
||||||
frame.setAlwaysOnTop(true);
|
tabbedPane.setEnabledAt(1, false);
|
||||||
frame.setVisible(true);
|
tabbedPane.setEnabledAt(2, false);
|
||||||
}
|
|
||||||
|
tabbedPane.setPreferredSize(new Dimension(900, 400));
|
||||||
public static void main(String[] args) {
|
|
||||||
RipperWindow ripperWindow = new RipperWindow();
|
frame.add(tabbedPane);
|
||||||
}
|
|
||||||
|
frame.pack();
|
||||||
private BaseRipperPanel createShowsPanel(){
|
frame.setAlwaysOnTop(true);
|
||||||
List<WebElement> shows = CrawlerUtil.getShows();
|
frame.setVisible(true);
|
||||||
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);
|
/**
|
||||||
|
* The main method to start the program
|
||||||
brp.getJButton().addActionListener((ActionEvent e) -> {
|
* @param args the arguments
|
||||||
panel3 = new JPanel();
|
*/
|
||||||
tabbedPane.setEnabledAt(2, false);
|
public static void main(String[] args) {
|
||||||
panel1 = createSeasonsPanel(brp.getCurrentSelected()).getJPanel();
|
RipperWindow ripperWindow = new RipperWindow();
|
||||||
tabbedPane.setComponentAt(1, panel1);
|
}
|
||||||
tabbedPane.setEnabledAt(1, true);
|
|
||||||
tabbedPane.setSelectedIndex(1);
|
/**
|
||||||
});
|
* Create a show panel listing all available shows
|
||||||
|
* @return the shopw panel
|
||||||
return brp;
|
*/
|
||||||
}
|
private BaseRipperPanel createShowsPanel(){
|
||||||
|
List<WebElement> shows = CrawlerUtil.getShows();
|
||||||
private BaseRipperPanel createSeasonsPanel(String url){
|
Map<String, String> namesToUrls = shows.stream().collect(Collectors.toMap(e -> e.findElement(By.xpath(".//img")).getAttribute("alt") , e -> e.getAttribute("href")));
|
||||||
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(namesToUrls);
|
||||||
|
|
||||||
BaseRipperPanel brp = new BaseRipperPanel(elements);
|
brp.getJButton().addActionListener((ActionEvent e) -> {
|
||||||
|
panel3 = new JPanel();
|
||||||
brp.getJButton().addActionListener((ActionEvent e) -> {
|
tabbedPane.setEnabledAt(2, false);
|
||||||
tabbedPane.setEnabledAt(2, false);
|
panel1 = createSeasonsPanel(brp.getCurrentSelected()).getJPanel();
|
||||||
panel2 = createEpisodesPanel(url,brp.getCurrentSelected()).getJPanel();
|
tabbedPane.setComponentAt(1, panel1);
|
||||||
tabbedPane.setComponentAt(2, panel2);
|
tabbedPane.setEnabledAt(1, true);
|
||||||
tabbedPane.setEnabledAt(2, true);
|
tabbedPane.setSelectedIndex(1);
|
||||||
tabbedPane.setSelectedIndex(2);
|
});
|
||||||
});
|
|
||||||
|
return brp;
|
||||||
return brp;
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
private EpisodesRipperPanel createEpisodesPanel(String url, String seasonName){
|
* Create a seaon panel for a selected show
|
||||||
List<EpisodeWrapper> episodes = CrawlerUtil.getEpisodes(url, seasonName);
|
* @param url the URL to the show
|
||||||
|
* @return the season panel
|
||||||
EpisodesRipperPanel erp = new EpisodesRipperPanel(episodes);
|
*/
|
||||||
|
private BaseRipperPanel createSeasonsPanel(String url){
|
||||||
erp.getDownloadButton().addActionListener(new ActionListener() {
|
List<WebElement> seasons = CrawlerUtil.getSeasons(url);
|
||||||
@Override
|
Map<String, String> elements = seasons.stream().collect(Collectors.toMap(e -> e.getText(), e -> e.getText()));
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
drp.addTask(new DownloadTask(erp.getCurrentSelected()));
|
BaseRipperPanel brp = new BaseRipperPanel(elements);
|
||||||
}
|
|
||||||
});
|
brp.getJButton().addActionListener((ActionEvent e) -> {
|
||||||
|
tabbedPane.setEnabledAt(2, false);
|
||||||
erp.getDownloadAllButton().addActionListener(new ActionListener() {
|
panel2 = createEpisodesPanel(url,brp.getCurrentSelected()).getJPanel();
|
||||||
@Override
|
tabbedPane.setComponentAt(2, panel2);
|
||||||
public void actionPerformed(ActionEvent e) {
|
tabbedPane.setEnabledAt(2, true);
|
||||||
episodes.forEach(d -> drp.addTask(new DownloadTask(d)));
|
tabbedPane.setSelectedIndex(2);
|
||||||
}
|
});
|
||||||
});
|
|
||||||
return erp;
|
return brp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a episode panel
|
||||||
|
* @param url the URL to the episode list
|
||||||
|
* @param seasonName the name of the selected season
|
||||||
|
* @return the episode panel
|
||||||
|
*/
|
||||||
|
private EpisodesRipperPanel createEpisodesPanel(String url, String seasonName){
|
||||||
|
List<EpisodeWrapper> episodes = CrawlerUtil.getEpisodes(url, seasonName);
|
||||||
|
|
||||||
|
EpisodesRipperPanel erp = new EpisodesRipperPanel(episodes);
|
||||||
|
|
||||||
|
erp.getDownloadButton().addActionListener((ActionEvent e) -> {
|
||||||
|
drp.addTask(new DownloadTask(erp.getCurrentSelected()));
|
||||||
|
});
|
||||||
|
|
||||||
|
erp.getDownloadAllButton().addActionListener((ActionEvent e) -> {
|
||||||
|
episodes.forEach(d -> drp.addTask(new DownloadTask(d)));
|
||||||
|
});
|
||||||
|
return erp;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,90 +1,97 @@
|
|||||||
/*
|
/*
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
* To change this license header, choose License Headers in Project Properties.
|
||||||
* To change this template file, choose Tools | Templates
|
* To change this template file, choose Tools | Templates
|
||||||
* and open the template in the editor.
|
* and open the template in the editor.
|
||||||
*/
|
*/
|
||||||
package com.greinet.tvtotalripper.ui;
|
package com.greinet.tvtotalripper.ui;
|
||||||
|
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Font;
|
import java.awt.Font;
|
||||||
import java.awt.GridBagConstraints;
|
import java.awt.GridBagConstraints;
|
||||||
import java.awt.GridBagLayout;
|
import java.awt.GridBagLayout;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
import javax.swing.JFileChooser;
|
import javax.swing.JFileChooser;
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import javax.swing.JTextField;
|
import javax.swing.JTextField;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author agreiner
|
* @author agreiner
|
||||||
*/
|
*/
|
||||||
public class SettingsRipperPanel {
|
public class SettingsRipperPanel {
|
||||||
|
|
||||||
private JPanel panel;
|
/** The JPanel to visualize the settings */
|
||||||
private JLabel labelDownloadFolder;
|
private JPanel panel;
|
||||||
private JTextField textDownloadFolder;
|
/** The JLabel for the download folder selection */
|
||||||
private JFileChooser fileChooserDownloadFolder;
|
private final JLabel labelDownloadFolder;
|
||||||
public static File DOWNLOADFOLDER = new File(".");
|
/** The text field for the current download folder */
|
||||||
private JButton button;
|
private JTextField textDownloadFolder;
|
||||||
|
/** The file chooser to select the download folder */
|
||||||
public SettingsRipperPanel(){
|
private JFileChooser fileChooserDownloadFolder;
|
||||||
panel = new JPanel();
|
/** The download folder */
|
||||||
panel.setLayout(new GridBagLayout());
|
public static File DOWNLOADFOLDER = new File(".");
|
||||||
|
/** The JButton to open the file chooser dialog */
|
||||||
GridBagConstraints gbc = new GridBagConstraints();
|
private final JButton button;
|
||||||
gbc.anchor = GridBagConstraints.NORTHWEST;
|
|
||||||
|
/**
|
||||||
gbc.gridx=0;
|
* Create a settings panel
|
||||||
gbc.gridy=0;
|
*/
|
||||||
gbc.gridwidth=1;
|
public SettingsRipperPanel(){
|
||||||
gbc.insets.bottom = 10;
|
panel = new JPanel();
|
||||||
labelDownloadFolder = new JLabel("Download folder");
|
panel.setLayout(new GridBagLayout());
|
||||||
labelDownloadFolder.setFont(new Font(labelDownloadFolder.getFont().getFontName(), Font.BOLD, 15));
|
|
||||||
panel.add(labelDownloadFolder, gbc);
|
GridBagConstraints gbc = new GridBagConstraints();
|
||||||
|
gbc.anchor = GridBagConstraints.NORTHWEST;
|
||||||
gbc.gridx=0;
|
|
||||||
gbc.gridy=1;
|
gbc.gridx=0;
|
||||||
gbc.gridwidth=8;
|
gbc.gridy=0;
|
||||||
gbc.insets.left = 20;
|
gbc.gridwidth=1;
|
||||||
textDownloadFolder = new JTextField(DOWNLOADFOLDER.getAbsolutePath());
|
gbc.insets.bottom = 10;
|
||||||
textDownloadFolder.setEditable(false);
|
labelDownloadFolder = new JLabel("Download folder");
|
||||||
textDownloadFolder.setPreferredSize(new Dimension(500, 30));
|
labelDownloadFolder.setFont(new Font(labelDownloadFolder.getFont().getFontName(), Font.BOLD, 15));
|
||||||
panel.add(textDownloadFolder,gbc);
|
panel.add(labelDownloadFolder, gbc);
|
||||||
|
|
||||||
|
gbc.gridx=0;
|
||||||
|
gbc.gridy=1;
|
||||||
|
gbc.gridwidth=8;
|
||||||
|
gbc.insets.left = 20;
|
||||||
gbc.gridx=8;
|
textDownloadFolder = new JTextField(DOWNLOADFOLDER.getAbsolutePath());
|
||||||
gbc.gridy=1;
|
textDownloadFolder.setEditable(false);
|
||||||
gbc.gridwidth=1;
|
textDownloadFolder.setPreferredSize(new Dimension(500, 30));
|
||||||
gbc.insets.left = 0;
|
panel.add(textDownloadFolder,gbc);
|
||||||
button = new JButton("...");
|
|
||||||
panel.add(button,gbc);
|
gbc.gridx=8;
|
||||||
|
gbc.gridy=1;
|
||||||
fileChooserDownloadFolder = new JFileChooser(DOWNLOADFOLDER);
|
gbc.gridwidth=1;
|
||||||
fileChooserDownloadFolder.setDialogTitle("Select download folder");
|
gbc.insets.left = 0;
|
||||||
fileChooserDownloadFolder.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
|
button = new JButton("...");
|
||||||
fileChooserDownloadFolder.setAcceptAllFileFilterUsed(false);
|
panel.add(button,gbc);
|
||||||
|
|
||||||
|
fileChooserDownloadFolder = new JFileChooser(DOWNLOADFOLDER);
|
||||||
button.addActionListener(e -> {
|
fileChooserDownloadFolder.setDialogTitle("Select download folder");
|
||||||
int value = fileChooserDownloadFolder.showOpenDialog(panel);
|
fileChooserDownloadFolder.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
|
||||||
|
fileChooserDownloadFolder.setAcceptAllFileFilterUsed(false);
|
||||||
if (value == JFileChooser.APPROVE_OPTION){
|
|
||||||
DOWNLOADFOLDER = fileChooserDownloadFolder.getSelectedFile();
|
|
||||||
textDownloadFolder.setText(DOWNLOADFOLDER.getAbsolutePath());
|
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;
|
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
}
|
* Get the JPanel
|
||||||
|
* @return the JPanel
|
||||||
|
*/
|
||||||
|
public JPanel getPanel() {
|
||||||
|
return panel;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user