- 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

@ -10,7 +10,36 @@
<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>
</properties>
</action>
<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> <exec.executable>java</exec.executable>
</properties> </properties>
</action> </action>

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,6 +1,5 @@
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;
@ -15,13 +14,11 @@ 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.AppleTVEpisodeBox;
import org.mp4parser.boxes.apple.AppleTVEpisodeNumberBox; import org.mp4parser.boxes.apple.AppleTVEpisodeNumberBox;
import org.mp4parser.boxes.apple.AppleTVSeasonBox;
import org.mp4parser.boxes.apple.AppleTVShowBox; import org.mp4parser.boxes.apple.AppleTVShowBox;
/** /**
* Change metadata and make sure chunkoffsets are corrected. * 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 { public class MetaDataWriter {
@ -43,7 +40,6 @@ public class MetaDataWriter {
return write; return write;
} }
private boolean needsOffsetCorrection(IsoFile isoFile) { private boolean needsOffsetCorrection(IsoFile isoFile) {
if (Path.getPath(isoFile, "moov[0]/mvex[0]") != null) { if (Path.getPath(isoFile, "moov[0]/mvex[0]") != null) {
// Fragmented files don't need a correction // Fragmented files don't need a correction
@ -62,6 +58,15 @@ public class MetaDataWriter {
} }
} }
/**
* Write itunes metadata to a mp4 file
* @param videoFilePath the file path
* @param title the title, or <code>null</code>
* @param artist the artist, or <code>null</code>
* @param show the show, or <code>null</code>
* @param episode the episode, or <code>null</code>
* @throws IOException error heandling mp4 file
*/
public void writeMetadata(String videoFilePath, String title, String artist, String show, String episode) throws IOException { public void writeMetadata(String videoFilePath, String title, String artist, String show, String episode) throws IOException {
File videoFile = new File(videoFilePath); File videoFile = new File(videoFilePath);
@ -160,8 +165,6 @@ public class MetaDataWriter {
ilst.addBox(sh); ilst.addBox(sh);
} }
long sizeAfter = moov.getSize(); long sizeAfter = moov.getSize();
long diff = sizeAfter - sizeBefore; long diff = sizeAfter - sizeBefore;
// This is the difference of before/after // This is the difference of before/after
@ -193,7 +196,7 @@ public class MetaDataWriter {
fc.close(); fc.close();
} }
FreeBox findFreeBox(Container c) { private FreeBox findFreeBox(Container c) {
for (Box box : c.getBoxes()) { for (Box box : c.getBoxes()) {
if (box instanceof FreeBox) { if (box instanceof FreeBox) {
return (FreeBox) box; return (FreeBox) box;

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

@ -33,11 +33,12 @@ public class CrawlerUtil {
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 */
private static String show = "";
public static String show = "";
/** /**
* Create a basic ChromeDriver * Create a basic ChromeDriver
* @return * @return the WebDriver
*/ */
private static WebDriver createChromeDriver(){ private static WebDriver createChromeDriver(){
ChromeOptions options = new ChromeOptions(); ChromeOptions options = new ChromeOptions();
@ -55,6 +56,10 @@ public class CrawlerUtil {
return driver; return driver;
} }
/**
* Create the WebDriver to get the video URLs from FetchFile.net
* @return the WebDriver
*/
private static WebDriver createFetchFileDriver(){ private static WebDriver createFetchFileDriver(){
WebDriver driver = createChromeDriver(); WebDriver driver = createChromeDriver();
driver.get("https://de.fetchfile.net/herunterladen-von-myspass"); driver.get("https://de.fetchfile.net/herunterladen-von-myspass");
@ -154,6 +159,11 @@ public class CrawlerUtil {
} }
} }
/**
* Returns the FetchFile.net URL to the myspass.com video file
* @param myspassURL the URL to the myspass.com video
* @return the FetchFile.net URL
*/
public static String getFetchfileURL(String myspassURL){ public static String getFetchfileURL(String myspassURL){
synchronized(driverFetchFile){ synchronized(driverFetchFile){
WebElement videoPathInput = driverFetchFile.findElement(By.id("videoPath")); WebElement videoPathInput = driverFetchFile.findElement(By.id("videoPath"));

View File

@ -10,25 +10,25 @@ import java.util.UUID;
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
@ -47,10 +47,6 @@ public class EpisodeWrapper {
return show; return show;
} }
public void setShow(String show) {
this.show = show;
}
/** /**
* Return the unique id * Return the unique id
* @return the id * @return the id
@ -67,14 +63,6 @@ public class EpisodeWrapper {
return url; return url;
} }
/**
* Set the URL to the episode
* @param url the URL
*/
public void setUrl(String url) {
this.url = url;
}
/** /**
* Get the title of the episode * Get the title of the episode
* @return the title * @return the title
@ -83,14 +71,6 @@ public class EpisodeWrapper {
return title; return title;
} }
/**
* Set the title of the episode
* @param title the title
*/
public void setTitle(String title) {
this.title = title;
}
/** /**
* Get the episode * Get the episode
* @return the episode * @return the episode
@ -99,14 +79,6 @@ public class EpisodeWrapper {
return episode; return episode;
} }
/**
* Set the episode number
* @param episode the episode
*/
public void setEpisodeNumber(String episode) {
this.episode = episode;
}
/** /**
* Get the season of the episode * Get the season of the episode
* @return the season * @return the season
@ -115,14 +87,6 @@ public class EpisodeWrapper {
return season; return season;
} }
/**
* Set the season of the episode
* @param season the season
*/
public void setSeason(String season) {
this.season = season;
}
/** /**
* Get the duration of the episode * Get the duration of the episode
* @return the duration * @return the duration
@ -131,14 +95,6 @@ public class EpisodeWrapper {
return duration; return duration;
} }
/**
* Set the duration of the episode
* @param duration the duration
*/
public void setDuration(String duration) {
this.duration = duration;
}
@Override @Override
public String toString() { public String toString() {
return title; return title;

View File

@ -16,11 +16,18 @@ import java.net.URL;
*/ */
public class ConnectionUtil { public class ConnectionUtil {
/** The connection to the download URL */
private HttpURLConnection httpConn; private HttpURLConnection httpConn;
/** The input stream from the connection */
private InputStream inputStream; private InputStream inputStream;
private String fileName; /** The download size */
private int contentLength; private int contentLength;
/**
* Prepare the download connection and open the input stream
* @param fileUrl the URL to the file
* @throws IOException error preparing download
*/
public void prepare(String fileUrl) throws IOException{ public void prepare(String fileUrl) throws IOException{
URL url = new URL(fileUrl); URL url = new URL(fileUrl);
httpConn = (HttpURLConnection) url.openConnection(); httpConn = (HttpURLConnection) url.openConnection();
@ -28,29 +35,7 @@ public class ConnectionUtil {
// always check HTTP response code first // always check HTTP response code first
if (responseCode == HttpURLConnection.HTTP_OK) { if (responseCode == HttpURLConnection.HTTP_OK) {
String disposition = httpConn.getHeaderField("Content-Disposition");
String contentType = httpConn.getContentType();
contentLength = httpConn.getContentLength(); contentLength = httpConn.getContentLength();
if (disposition != null) {
// extracts file name from header field
int index = disposition.indexOf("filename=");
if (index > 0) {
fileName = disposition.substring(index + 10,
disposition.length() - 1);
}
} else {
// extracts file name from URL
fileName = fileUrl.substring(fileUrl.lastIndexOf("/") + 1,
fileUrl.length());
}
// output for debugging purpose only
System.out.println("Content-Type = " + contentType);
System.out.println("Content-Disposition = " + disposition);
System.out.println("Content-Length = " + contentLength);
System.out.println("fileName = " + fileName);
// opens input stream from the HTTP connection // opens input stream from the HTTP connection
inputStream = httpConn.getInputStream(); inputStream = httpConn.getInputStream();
@ -61,19 +46,27 @@ public class ConnectionUtil {
} }
} }
/**
* Close the inout stream and disconnect the download
* @throws IOException
*/
public void disconnect() throws IOException { public void disconnect() throws IOException {
inputStream.close(); inputStream.close();
httpConn.disconnect(); httpConn.disconnect();
} }
public String getFileName() { /**
return this.fileName; * Get the content length of the download
} * @return the content length
*/
public int getContentLength() { public int getContentLength() {
return this.contentLength; return this.contentLength;
} }
/**
* Get the download related inout stream
* @return the input stream
*/
public InputStream getInputStream() { public InputStream getInputStream() {
return this.inputStream; return this.inputStream;
} }

View File

@ -1,6 +1,5 @@
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;
@ -11,19 +10,27 @@ 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> {
private static final int BUFFER_SIZE = 4096;
/** The download buffer size */
private static final int BUFFER_SIZE = 4096;
/** The episode of the downloaded file */
private final EpisodeWrapper episodeWrapper; private final EpisodeWrapper episodeWrapper;
/**
* Create a download task for a episode
* @param episodeWrapper
*/
public DownloadTask(EpisodeWrapper episodeWrapper) { public DownloadTask(EpisodeWrapper episodeWrapper) {
this.episodeWrapper = episodeWrapper; this.episodeWrapper = episodeWrapper;
} }
/**
* Get the episode of the download
* @return the episode
*/
public EpisodeWrapper getEpisodeWrapper() { public EpisodeWrapper getEpisodeWrapper() {
return episodeWrapper; return episodeWrapper;
} }
@ -33,11 +40,14 @@ public class DownloadTask extends SwingWorker<Void, Void> {
return episodeWrapper.getTitle(); return episodeWrapper.getTitle();
} }
public long fileSize = 0; /** The bytes size of the file to download */
private long fileSize = 0;
public long totalBytesRead = 0; /** the amount of downloaded bytes */
private long totalBytesRead = 0;
public int percentCompleted = 0; /** The completion percentage */
private int percentCompleted = 0;
/** /**
* Executed in background thread * Executed in background thread
@ -55,29 +65,30 @@ public class DownloadTask extends SwingWorker<Void, Void> {
String fixedTitle = episodeWrapper.getTitle().replace(":", " "); String fixedTitle = episodeWrapper.getTitle().replace(":", " ");
File outputFile = new File(SettingsRipperPanel.DOWNLOADFOLDER, fixedTitle+".mp4"); File outputFile = new File(SettingsRipperPanel.DOWNLOADFOLDER, fixedTitle+".mp4");
FileOutputStream outputStream = new FileOutputStream(outputFile); try (FileOutputStream outputStream = new FileOutputStream(outputFile)) {
byte[] buffer = new byte[BUFFER_SIZE]; byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead = -1; int bytesRead;
totalBytesRead = 0; totalBytesRead = 0;
percentCompleted = 0; percentCompleted = 0;
int oldPercentCompleted = 0; int oldPercentCompleted = 0;
fileSize = util.getContentLength(); fileSize = util.getContentLength();
while ((bytesRead = inputStream.read(buffer)) != -1) { while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead); outputStream.write(buffer, 0, bytesRead);
totalBytesRead += bytesRead; totalBytesRead += bytesRead;
percentCompleted = (int) (totalBytesRead * 100 / fileSize); percentCompleted = (int) (totalBytesRead * 100 / fileSize);
firePropertyChange(Long.toString(episodeWrapper.getId()), oldPercentCompleted, percentCompleted); firePropertyChange(Long.toString(episodeWrapper.getId()), oldPercentCompleted, percentCompleted);
oldPercentCompleted = percentCompleted; oldPercentCompleted = percentCompleted;
//setProgress(percentCompleted); //setProgress(percentCompleted);
}
} }
outputStream.close();
util.disconnect(); util.disconnect();
MetaDataWriter mdp = new MetaDataWriter();
mdp.writeMetadata(outputFile.getAbsolutePath(), episodeWrapper.getTitle(), episodeWrapper.getShow(), episodeWrapper.getShow(), episodeWrapper.getEpisode()); // Enable MetaDataWriter
// MetaDataWriter mdp = new MetaDataWriter();
// mdp.writeMetadata(outputFile.getAbsolutePath(), episodeWrapper.getTitle(), episodeWrapper.getShow(), episodeWrapper.getShow(), episodeWrapper.getEpisode());
} catch (IOException ex) { } catch (IOException ex) {
cancel(true); cancel(true);
@ -86,7 +97,31 @@ public class DownloadTask extends SwingWorker<Void, Void> {
} }
/** /**
* Executed in Swing's event dispatching thread * 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 @Override
protected void done() { protected void done() {

View File

@ -17,18 +17,27 @@ 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;
/** The Jlist for the elements */
private JList<String> listPanel; private JList<String> listPanel;
/** The text field for the currently selected element */
private JTextField textField; private JTextField textField;
/** The button to select the currently selected element */
private final JButton button; private final JButton button;
/** The label for the element count */
private final JLabel label; private final JLabel label;
private Map<String, String> elements; /** The map for the elements */
private final Map<String, String> elements;
/**
* Create a base panel the display elements and select a specific one
* @param elements the elements
*/
public BaseRipperPanel(Map<String, String> elements){ public BaseRipperPanel(Map<String, String> elements){
this.elements = elements; this.elements = elements;
@ -97,18 +106,34 @@ public class BaseRipperPanel {
} }
/**
* Get the JPanel
* @return the JPanel
*/
public JPanel getJPanel(){ public JPanel getJPanel(){
return panel; return panel;
} }
/**
* Get the selection JButton
* @return the JButton
*/
public JButton getJButton(){ public JButton getJButton(){
return button; return button;
} }
/**
* Get the String representing the currently selected element
* @return the selected element
*/
public String getCurrentSelected(){ public String getCurrentSelected(){
return elements.get(listPanel.getSelectedValue()); return elements.get(listPanel.getSelectedValue());
} }
/**
* Get the Jlist of the elements
* @return the JList
*/
public JList getJList(){ public JList getJList(){
return listPanel; return listPanel;
} }

View File

@ -1,8 +1,3 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.greinet.tvtotalripper.ui; package com.greinet.tvtotalripper.ui;
import com.greinet.tvtotalripper.download.DownloadTask; import com.greinet.tvtotalripper.download.DownloadTask;
@ -25,21 +20,28 @@ import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionEvent;
/** /**
* * A download panel to show the current downloads
* @author agreiner
*/ */
public class DownloadRipperPanel implements PropertyChangeListener{ public class DownloadRipperPanel implements PropertyChangeListener{
/** The JPanel for download task visualization */
private final JPanel panel; private final JPanel panel;
/** The JList for the downloads */
private JList<DownloadTask> listTasks; private JList<DownloadTask> listTasks;
private DefaultListModel<DownloadTask> listModel; /** The ListModel for the DownloadTasks */
private final DefaultListModel<DownloadTask> listModel;
/** The text field for the currently selected task */
private JTextField textSelectedTask; private JTextField textSelectedTask;
/** The JLabel for the DownloadTask count */
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;
/**
* Create a download panel
*/
public DownloadRipperPanel(){ public DownloadRipperPanel(){
downloadTasks = new ArrayList<>(); downloadTasks = new ArrayList<>();
@ -102,14 +104,26 @@ public class DownloadRipperPanel implements PropertyChangeListener{
panel.add(labelTaskCount, c); panel.add(labelTaskCount, c);
} }
/**
* Get the JPanel
* @return the JPanel
*/
public JPanel getJPanel(){ public JPanel getJPanel(){
return panel; return panel;
} }
/**
* Get the currently selected task
* @return the selected task
*/
public DownloadTask getSelectedTask(){ public DownloadTask getSelectedTask(){
return listTasks.getSelectedValue(); return listTasks.getSelectedValue();
} }
/**
* Add a download task to be executed
* @param task
*/
public void addTask(DownloadTask task){ public void addTask(DownloadTask task){
downloadTasks.add(task); downloadTasks.add(task);
task.execute(); task.execute();
@ -119,6 +133,10 @@ public class DownloadRipperPanel implements PropertyChangeListener{
} }
/**
* Listen to the property change events and remove a task if the new value is 101
* @param evt the event parameters
*/
@Override @Override
public void propertyChange(PropertyChangeEvent evt) { public void propertyChange(PropertyChangeEvent evt) {
if(evt.getNewValue().equals(101)){ if(evt.getNewValue().equals(101)){
@ -137,6 +155,9 @@ public class DownloadRipperPanel implements PropertyChangeListener{
panel.repaint(); panel.repaint();
} }
/**
* Update the current task count
*/
private void updateTaskCount(){ private void updateTaskCount(){
labelTaskCount.setText(Integer.toString(listTasks.getModel().getSize())); labelTaskCount.setText(Integer.toString(listTasks.getModel().getSize()));
} }

View File

@ -8,7 +8,6 @@ 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;
@ -21,22 +20,37 @@ import org.apache.commons.io.FileUtils;
*/ */
public class DownloadTaskInformationPanel extends JPanel{ public class DownloadTaskInformationPanel extends JPanel{
private JLabel labelTitle; /** The label for the title */
private JLabel labelShow; private final JLabel labelTitle;
private JLabel labelSeason; /** The label for the show */
private JLabel labelEpisode; private final JLabel labelShow;
private JLabel labelDuration; /** The label for the season */
private JLabel labelProgress; private final JLabel labelSeason;
/** The label for the episode */
private JTextField textTitle; private final JLabel labelEpisode;
private JTextField textShow; /** The label for the duration */
private JTextField textSeason; private final JLabel labelDuration;
private JTextField textEpisode; /** The label for the download progress */
private JTextField textDuration; private final JLabel labelProgress;
private JProgressBar progressBar; /** The text field for the title */
private final JTextField textTitle;
/** The text field for the show */
private final JTextField textShow;
/** The text field for the season */
private final JTextField textSeason;
/** The text field for the episode */
private final JTextField textEpisode;
/** The text field for the duration */
private final JTextField textDuration;
/** The text field for the download progress */
private final JProgressBar progressBar;
/** The currently visualized download task */
public DownloadTask selectedDownloadTask; public DownloadTask selectedDownloadTask;
/**
* Create a Panel for DownloadTask information visualization
*/
public DownloadTaskInformationPanel(){ public DownloadTaskInformationPanel(){
labelTitle = new JLabel("Title"); labelTitle = new JLabel("Title");
@ -79,6 +93,9 @@ public class DownloadTaskInformationPanel extends JPanel{
add(progressBar); add(progressBar);
} }
/**
* Clear all information
*/
public void clearInformation(){ public void clearInformation(){
this.selectedDownloadTask = null; this.selectedDownloadTask = null;
textTitle.setText(""); textTitle.setText("");
@ -90,6 +107,10 @@ public class DownloadTaskInformationPanel extends JPanel{
progressBar.setString(""); progressBar.setString("");
} }
/**
* Set the information of the download task
* @param downloadTask the download task
*/
public void setInformation(DownloadTask downloadTask){ public void setInformation(DownloadTask downloadTask){
textTitle.setText(downloadTask.getEpisodeWrapper().getTitle()); textTitle.setText(downloadTask.getEpisodeWrapper().getTitle());
textTitle.setEditable(false); textTitle.setEditable(false);
@ -102,10 +123,10 @@ public class DownloadTaskInformationPanel extends JPanel{
textDuration.setText(downloadTask.getEpisodeWrapper().getDuration()); textDuration.setText(downloadTask.getEpisodeWrapper().getDuration());
if(selectedDownloadTask != null && selectedDownloadTask.equals(downloadTask)){ if(selectedDownloadTask != null && selectedDownloadTask.equals(downloadTask)){
progressBar.setValue(downloadTask.percentCompleted); progressBar.setValue(downloadTask.getPercentCompleted());
progressBar.setStringPainted(true); progressBar.setStringPainted(true);
String done = FileUtils.byteCountToDisplaySize(downloadTask.totalBytesRead); String done = FileUtils.byteCountToDisplaySize(downloadTask.getTotalBytesRead());
String todo = FileUtils.byteCountToDisplaySize(downloadTask.fileSize); String todo = FileUtils.byteCountToDisplaySize(downloadTask.getFileSize());
progressBar.setString(done+"/"+todo); progressBar.setString(done+"/"+todo);
} }
@ -114,8 +135,8 @@ public class DownloadTaskInformationPanel extends JPanel{
if(selectedDownloadTask != null && evt.getPropertyName().equals(Long.toString(selectedDownloadTask.getEpisodeWrapper().getId()))){ if(selectedDownloadTask != null && evt.getPropertyName().equals(Long.toString(selectedDownloadTask.getEpisodeWrapper().getId()))){
progressBar.setValue((int)evt.getNewValue()); progressBar.setValue((int)evt.getNewValue());
progressBar.setStringPainted(true); progressBar.setStringPainted(true);
String done = FileUtils.byteCountToDisplaySize(downloadTask.totalBytesRead); String done = FileUtils.byteCountToDisplaySize(downloadTask.getTotalBytesRead());
String todo = FileUtils.byteCountToDisplaySize(downloadTask.fileSize); String todo = FileUtils.byteCountToDisplaySize(downloadTask.getFileSize());
progressBar.setString(done+"/"+todo); progressBar.setString(done+"/"+todo);
} }
}); });

View File

@ -16,23 +16,30 @@ 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;
/** The button to start the episode download */
private final JButton buttonDownload; private final JButton buttonDownload;
/** The label for the episode count */
private final JLabel label; private final JLabel label;
/** The list of episodes */
private final List<EpisodeWrapper> episodes; private final List<EpisodeWrapper> episodes;
/** The button to download all episodes in the list */
private final JButton buttonDownloadAll; private final JButton buttonDownloadAll;
/**
* Create a panel to display episodes in a list
* @param episodes the episodes to display
*/
public EpisodesRipperPanel(List<EpisodeWrapper> episodes){ public EpisodesRipperPanel(List<EpisodeWrapper> episodes){
this.episodes = episodes; this.episodes = episodes;
@ -104,19 +111,34 @@ public class EpisodesRipperPanel {
} }
/**
* Get the JPanel
* @return the JPanel
*/
public JPanel getJPanel(){ public JPanel getJPanel(){
return panel; return panel;
} }
/**
* Get the currently selecte episode
* @return
*/
public EpisodeWrapper getCurrentSelected(){ public EpisodeWrapper getCurrentSelected(){
return listPanel.getSelectedValue(); return listPanel.getSelectedValue();
} }
/**
* Get the download button
* @return the download button
*/
public JButton getDownloadButton(){ public JButton getDownloadButton(){
return buttonDownload; return buttonDownload;
} }
/**
* Get the download all button
* @return the download all button
*/
public JButton getDownloadAllButton(){ public JButton getDownloadAllButton(){
return buttonDownloadAll; return buttonDownloadAll;
} }

View File

@ -21,22 +21,30 @@ 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;
/** The panel for the episodes */
private JComponent panel3; private JComponent panel3;
private JComponent panel4; /** The panel for the downloads */
private JComponent panel5; private final JComponent panel4;
/** The panel for the settings */
private BaseRipperPanel showRipperPanel; private final JComponent panel5;
private SettingsRipperPanel srp; /** The show panel */
private DownloadRipperPanel drp; private final BaseRipperPanel showRipperPanel;
/** The settings panel */
private final SettingsRipperPanel srp;
/** The downloads panel */
private final DownloadRipperPanel drp;
/**
* Create the main application UI
*/
public RipperWindow(){ public RipperWindow(){
frame = new JFrame("TV Total Ripper"); JFrame frame = new JFrame("TV Total Ripper");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
tabbedPane = new JTabbedPane(); tabbedPane = new JTabbedPane();
@ -69,10 +77,18 @@ public class RipperWindow {
frame.setVisible(true); frame.setVisible(true);
} }
/**
* The main method to start the program
* @param args the arguments
*/
public static void main(String[] args) { public static void main(String[] args) {
RipperWindow ripperWindow = new RipperWindow(); RipperWindow ripperWindow = new RipperWindow();
} }
/**
* Create a show panel listing all available shows
* @return the shopw panel
*/
private BaseRipperPanel createShowsPanel(){ private BaseRipperPanel createShowsPanel(){
List<WebElement> shows = CrawlerUtil.getShows(); List<WebElement> shows = CrawlerUtil.getShows();
Map<String, String> namesToUrls = shows.stream().collect(Collectors.toMap(e -> e.findElement(By.xpath(".//img")).getAttribute("alt") , e -> e.getAttribute("href"))); Map<String, String> namesToUrls = shows.stream().collect(Collectors.toMap(e -> e.findElement(By.xpath(".//img")).getAttribute("alt") , e -> e.getAttribute("href")));
@ -91,6 +107,11 @@ public class RipperWindow {
return brp; return brp;
} }
/**
* Create a seaon panel for a selected show
* @param url the URL to the show
* @return the season panel
*/
private BaseRipperPanel createSeasonsPanel(String url){ private BaseRipperPanel createSeasonsPanel(String url){
List<WebElement> seasons = CrawlerUtil.getSeasons(url); List<WebElement> seasons = CrawlerUtil.getSeasons(url);
Map<String, String> elements = seasons.stream().collect(Collectors.toMap(e -> e.getText(), e -> e.getText())); Map<String, String> elements = seasons.stream().collect(Collectors.toMap(e -> e.getText(), e -> e.getText()));
@ -108,23 +129,23 @@ public class RipperWindow {
return brp; 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){ private EpisodesRipperPanel createEpisodesPanel(String url, String seasonName){
List<EpisodeWrapper> episodes = CrawlerUtil.getEpisodes(url, seasonName); List<EpisodeWrapper> episodes = CrawlerUtil.getEpisodes(url, seasonName);
EpisodesRipperPanel erp = new EpisodesRipperPanel(episodes); EpisodesRipperPanel erp = new EpisodesRipperPanel(episodes);
erp.getDownloadButton().addActionListener(new ActionListener() { erp.getDownloadButton().addActionListener((ActionEvent e) -> {
@Override drp.addTask(new DownloadTask(erp.getCurrentSelected()));
public void actionPerformed(ActionEvent e) {
drp.addTask(new DownloadTask(erp.getCurrentSelected()));
}
}); });
erp.getDownloadAllButton().addActionListener(new ActionListener() { erp.getDownloadAllButton().addActionListener((ActionEvent e) -> {
@Override episodes.forEach(d -> drp.addTask(new DownloadTask(d)));
public void actionPerformed(ActionEvent e) {
episodes.forEach(d -> drp.addTask(new DownloadTask(d)));
}
}); });
return erp; return erp;
} }

View File

@ -22,13 +22,22 @@ import javax.swing.JTextField;
*/ */
public class SettingsRipperPanel { public class SettingsRipperPanel {
/** The JPanel to visualize the settings */
private JPanel panel; private JPanel panel;
private JLabel labelDownloadFolder; /** The JLabel for the download folder selection */
private final JLabel labelDownloadFolder;
/** The text field for the current download folder */
private JTextField textDownloadFolder; private JTextField textDownloadFolder;
/** The file chooser to select the download folder */
private JFileChooser fileChooserDownloadFolder; private JFileChooser fileChooserDownloadFolder;
/** The download folder */
public static File DOWNLOADFOLDER = new File("."); public static File DOWNLOADFOLDER = new File(".");
private JButton button; /** The JButton to open the file chooser dialog */
private final JButton button;
/**
* Create a settings panel
*/
public SettingsRipperPanel(){ public SettingsRipperPanel(){
panel = new JPanel(); panel = new JPanel();
panel.setLayout(new GridBagLayout()); panel.setLayout(new GridBagLayout());
@ -53,10 +62,6 @@ public class SettingsRipperPanel {
textDownloadFolder.setPreferredSize(new Dimension(500, 30)); textDownloadFolder.setPreferredSize(new Dimension(500, 30));
panel.add(textDownloadFolder,gbc); panel.add(textDownloadFolder,gbc);
gbc.gridx=8; gbc.gridx=8;
gbc.gridy=1; gbc.gridy=1;
gbc.gridwidth=1; gbc.gridwidth=1;
@ -81,10 +86,12 @@ public class SettingsRipperPanel {
}); });
} }
/**
* Get the JPanel
* @return the JPanel
*/
public JPanel getPanel() { public JPanel getPanel() {
return panel; return panel;
} }
} }