- Deleted unused classes

- Refined and added javadoc
- Disabled MetaDataWriter
This commit is contained in:
Andreas Greiner 2021-04-05 23:14:41 +02:00
parent 34ea601a95
commit f8dd7be711
15 changed files with 1643 additions and 1938 deletions

View File

@ -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>

View File

@ -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);
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
} }

View File

@ -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);
}
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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()));
}
}

View File

@ -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);
}
});
}
}

View File

@ -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;
}
}

View File

@ -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;
}
} }

View File

@ -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;
}
}