parent
61fbd6a4e9
commit
3edd879d7e
23
build.xml
23
build.xml
|
@ -11,9 +11,11 @@
|
|||
|
||||
<property name="executable" location="${build}/${title}.jar" />
|
||||
|
||||
<property name="lib.nekohtml" value="nekohtml.jar" />
|
||||
<property name="lib.xerces" value="xercesImpl.jar" />
|
||||
<property name="lib.simmetrics" value="simmetrics.jar" />
|
||||
<property name="lib.nekohtml" value="nekohtml-1.9.6.1.jar" />
|
||||
<property name="lib.simmetrics" value="simmetrics_jar_v1_6_2_d07_02_07.jar" />
|
||||
<property name="lib.xmlrpc" value="xmlrpc-client-1.1.jar" />
|
||||
|
||||
|
||||
|
||||
<target name="jar" depends="build">
|
||||
|
@ -27,17 +29,26 @@
|
|||
</manifest>
|
||||
|
||||
<!-- include libs if fatjar is set -->
|
||||
<zipfileset src="${lib}/${lib.xerces}">
|
||||
<include if="fatjar" name="**/*.class" />
|
||||
<include if="fatjar" name="**/*.properties" />
|
||||
<exclude if="fatjar" name="**/*Test*" />
|
||||
</zipfileset>
|
||||
<zipfileset src="${lib}/${lib.nekohtml}">
|
||||
<include if="fatjar" name="**/*.class" />
|
||||
<include if="fatjar" name="**/*.properties" />
|
||||
<exclude if="fatjar" name="**/*Test*" />
|
||||
</zipfileset>
|
||||
<zipfileset src="${lib}/${lib.xerces}">
|
||||
<include if="fatjar" name="**/*.class" />
|
||||
</zipfileset>
|
||||
<zipfileset src="${lib}/${lib.simmetrics}">
|
||||
<include if="fatjar" name="**/*.class" />
|
||||
<include if="fatjar" name="**/*.properties" />
|
||||
<exclude if="fatjar" name="**/*Test*" />
|
||||
</zipfileset>
|
||||
</zipfileset>
|
||||
<zipfileset src="${lib}/${lib.xmlrpc}">
|
||||
<include if="fatjar" name="**/*.class" />
|
||||
<include if="fatjar" name="**/*.properties" />
|
||||
<exclude if="fatjar" name="**/*Test*" />
|
||||
</zipfileset>
|
||||
</jar>
|
||||
</target>
|
||||
|
||||
|
|
Binary file not shown.
|
@ -1,3 +1,6 @@
|
|||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
|
||||
|
@ -10,6 +13,8 @@ public class Main {
|
|||
* @param args
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).setLevel(Level.ALL);
|
||||
|
||||
try {
|
||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||
} catch (Exception e) {
|
||||
|
|
|
@ -7,7 +7,7 @@ import java.awt.Dimension;
|
|||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.io.File;
|
||||
import java.util.LinkedList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
|
@ -70,7 +70,7 @@ public class AnalyzePanel extends FileBotPanel {
|
|||
}
|
||||
};
|
||||
|
||||
private LinkedList<ToolPanel> toolPanels = new LinkedList<ToolPanel>();
|
||||
private List<ToolPanel> toolPanels = new ArrayList<ToolPanel>();
|
||||
|
||||
|
||||
public void addTool(ToolPanel toolPanel) {
|
||||
|
|
|
@ -4,9 +4,9 @@ package net.sourceforge.filebot.ui.panel.analyze.tools;
|
|||
|
||||
import java.awt.BorderLayout;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
|
@ -84,7 +84,7 @@ public class TypePanel extends ToolPanel {
|
|||
if (list != null)
|
||||
list.add(f);
|
||||
else {
|
||||
list = new LinkedList<File>();
|
||||
list = new ArrayList<File>();
|
||||
list.add(f);
|
||||
map.put(suffix, list);
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
package net.sourceforge.filebot.ui.panel.rename;
|
||||
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.DefaultListModel;
|
||||
|
@ -21,7 +21,7 @@ public class FilesRenameList extends RenameList {
|
|||
public List<FileEntry> getListEntries() {
|
||||
DefaultListModel model = getModel();
|
||||
|
||||
List<FileEntry> files = new LinkedList<FileEntry>();
|
||||
List<FileEntry> files = new ArrayList<FileEntry>();
|
||||
|
||||
for (int i = 0; i < model.getSize(); ++i)
|
||||
files.add((FileEntry) model.get(i));
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
package net.sourceforge.filebot.ui.panel.rename;
|
||||
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.DefaultListModel;
|
||||
|
@ -21,7 +21,7 @@ public class NamesRenameList extends RenameList {
|
|||
public List<ListEntry<?>> getListEntries() {
|
||||
DefaultListModel model = getModel();
|
||||
|
||||
List<ListEntry<?>> entries = new LinkedList<ListEntry<?>>();
|
||||
List<ListEntry<?>> entries = new ArrayList<ListEntry<?>>();
|
||||
|
||||
for (int i = 0; i < model.getSize(); i++)
|
||||
entries.add((ListEntry<?>) model.get(i));
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
package net.sourceforge.filebot.ui.panel.rename.match;
|
||||
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import net.sourceforge.filebot.ui.panel.rename.entry.ListEntry;
|
||||
|
@ -12,7 +12,7 @@ import net.sourceforge.filebot.ui.panel.rename.similarity.SimilarityMetric;
|
|||
public class Matcher {
|
||||
|
||||
public List<Match> match(List<? extends ListEntry<?>> listA, List<? extends ListEntry<?>> listB, SimilarityMetric similarityMetric) {
|
||||
LinkedList<Match> matches = new LinkedList<Match>();
|
||||
ArrayList<Match> matches = new ArrayList<Match>();
|
||||
|
||||
for (ListEntry<?> entryA : listA) {
|
||||
float maxSimilarity = -1;
|
||||
|
|
|
@ -12,7 +12,7 @@ import javax.swing.SwingWorker;
|
|||
|
||||
public class ChecksumComputationTask extends SwingWorker<Long, Object> {
|
||||
|
||||
private static final int CHUNK_SIZE = 32 * 1024;
|
||||
private static final int BUFFER_SIZE = 32 * 1024;
|
||||
|
||||
private File file;
|
||||
|
||||
|
@ -31,9 +31,7 @@ public class ChecksumComputationTask extends SwingWorker<Long, Object> {
|
|||
if (length > 0) {
|
||||
long done = 0;
|
||||
|
||||
int bufferLength = (int) Math.min(length, CHUNK_SIZE);
|
||||
|
||||
byte[] buffer = new byte[bufferLength];
|
||||
byte[] buffer = new byte[BUFFER_SIZE];
|
||||
|
||||
int bytesRead = 0;
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@ import java.text.NumberFormat;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
@ -39,9 +38,8 @@ public class AnidbClient extends EpisodeListClient {
|
|||
|
||||
@Override
|
||||
public List<String> search(String searchterm) throws IOException, SAXException {
|
||||
if (cache.containsKey(searchterm)) {
|
||||
if (cache.containsKey(searchterm))
|
||||
return Arrays.asList(searchterm);
|
||||
}
|
||||
|
||||
Document dom = HtmlUtil.getHtmlDocument(getSearchUrl(searchterm));
|
||||
|
||||
|
@ -97,7 +95,7 @@ public class AnidbClient extends EpisodeListClient {
|
|||
|
||||
List<Node> nodes = XPathUtil.selectNodes("//TABLE[@id='eplist']//TR/TD/SPAN/ancestor::TR", dom);
|
||||
|
||||
LinkedList<Episode> list = new LinkedList<Episode>();
|
||||
ArrayList<Episode> list = new ArrayList<Episode>(nodes.size());
|
||||
|
||||
NumberFormat f = NumberFormat.getInstance();
|
||||
f.setMinimumIntegerDigits(Math.max(Integer.toString(nodes.size()).length(), 2));
|
||||
|
|
|
@ -0,0 +1,159 @@
|
|||
|
||||
package net.sourceforge.filebot.web;
|
||||
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.channels.WritableByteChannel;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.swing.SwingWorker;
|
||||
|
||||
|
||||
public class DownloadTask extends SwingWorker<ByteBuffer, Object> {
|
||||
|
||||
public static final String DOWNLOAD_STATE = "download state";
|
||||
public static final String BYTES_READ = "bytes read";
|
||||
|
||||
|
||||
public static enum DownloadState {
|
||||
PENDING, CONNECTING, DOWNLOADING, DONE;
|
||||
}
|
||||
|
||||
private static final int BUFFER_SIZE = 4 * 1024;
|
||||
|
||||
private URL url;
|
||||
private ByteBuffer postdata = null;
|
||||
|
||||
private long size = -1;
|
||||
private long bytesRead = 0;
|
||||
private DownloadState state = DownloadState.PENDING;
|
||||
|
||||
|
||||
public DownloadTask(URL url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
|
||||
public DownloadTask(URL url, ByteBuffer postdata) {
|
||||
this.url = url;
|
||||
this.postdata = postdata;
|
||||
}
|
||||
|
||||
|
||||
public DownloadTask(URL url, Map<String, String> postdata) {
|
||||
this.url = url;
|
||||
this.postdata = encodeParameters(postdata);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected ByteBuffer doInBackground() throws Exception {
|
||||
setDownloadState(DownloadState.CONNECTING);
|
||||
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
|
||||
if (postdata != null) {
|
||||
connection.setRequestMethod("POST");
|
||||
connection.setDoOutput(true);
|
||||
WritableByteChannel out = Channels.newChannel(connection.getOutputStream());
|
||||
out.write(postdata);
|
||||
out.close();
|
||||
}
|
||||
|
||||
size = connection.getContentLength();
|
||||
|
||||
setDownloadState(DownloadState.DOWNLOADING);
|
||||
|
||||
InputStream in = connection.getInputStream();
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(BUFFER_SIZE);
|
||||
|
||||
byte[] buffer = new byte[BUFFER_SIZE];
|
||||
int len = 0;
|
||||
|
||||
try {
|
||||
while ((len = in.read(buffer)) > 0) {
|
||||
out.write(buffer, 0, len);
|
||||
|
||||
bytesRead += len;
|
||||
getPropertyChangeSupport().firePropertyChange(BYTES_READ, null, bytesRead);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// IOException (Premature EOF) is always thrown when the size of
|
||||
// the response body is not known in advance, so we ignore it
|
||||
if (isSizeKnown())
|
||||
throw e;
|
||||
|
||||
} finally {
|
||||
in.close();
|
||||
out.close();
|
||||
connection.disconnect();
|
||||
}
|
||||
|
||||
setDownloadState(DownloadState.DONE);
|
||||
return ByteBuffer.wrap(out.toByteArray());
|
||||
}
|
||||
|
||||
|
||||
private void setDownloadState(DownloadState state) {
|
||||
this.state = state;
|
||||
getPropertyChangeSupport().firePropertyChange(DOWNLOAD_STATE, null, state);
|
||||
}
|
||||
|
||||
|
||||
public DownloadState getDownloadState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
|
||||
public long getBytesRead() {
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
|
||||
public boolean isSizeKnown() {
|
||||
return size >= 0;
|
||||
}
|
||||
|
||||
|
||||
public long getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
private static ByteBuffer encodeParameters(Map<String, String> parameters) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
|
||||
int i = 0;
|
||||
|
||||
for (String key : parameters.keySet()) {
|
||||
if (i > 0)
|
||||
sb.append("&");
|
||||
|
||||
sb.append(key);
|
||||
sb.append("=");
|
||||
|
||||
try {
|
||||
sb.append(URLEncoder.encode(parameters.get(key), "UTF-8"));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
// will never happen
|
||||
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.WARNING, e.getMessage(), e);
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return Charset.forName("UTF-8").encode(sb.toString());
|
||||
}
|
||||
|
||||
}
|
|
@ -29,19 +29,19 @@ public class ImdbSearchEngine {
|
|||
private String host = "www.imdb.com";
|
||||
|
||||
|
||||
public List<Movie> search(String searchterm) throws IOException, SAXException {
|
||||
public List<MovieDescriptor> search(String searchterm) throws IOException, SAXException {
|
||||
|
||||
Document dom = HtmlUtil.getHtmlDocument(getSearchUrl(searchterm));
|
||||
|
||||
List<Node> nodes = XPathUtil.selectNodes("id('outerbody')//TABLE//P[position() >= 2 and position() <=3 ]//A[count(child::IMG) <= 0]/..", dom);
|
||||
|
||||
ArrayList<Movie> movies = new ArrayList<Movie>();
|
||||
ArrayList<MovieDescriptor> movies = new ArrayList<MovieDescriptor>();
|
||||
|
||||
for (Node node : nodes) {
|
||||
try {
|
||||
movies.add(parseMovie(node));
|
||||
movies.add(parseMovieNode(node));
|
||||
} catch (Exception e) {
|
||||
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.WARNING, "Invalid movie node", e);
|
||||
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.WARNING, "Cannot parse movie node", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -49,7 +49,7 @@ public class ImdbSearchEngine {
|
|||
}
|
||||
|
||||
|
||||
private Movie parseMovie(Node node) {
|
||||
private MovieDescriptor parseMovieNode(Node node) throws MalformedURLException {
|
||||
// ignore javascript links
|
||||
Node linkNode = XPathUtil.selectFirstNode("./A[count(@onclick) <= 0]", node);
|
||||
|
||||
|
@ -58,13 +58,12 @@ public class ImdbSearchEngine {
|
|||
|
||||
// match /title/tt0379786/
|
||||
Matcher idMatcher = Pattern.compile(".*/tt(\\d+)/.*").matcher(href);
|
||||
Integer imdbID = null;
|
||||
int imdbId;
|
||||
|
||||
if (idMatcher.matches()) {
|
||||
imdbID = new Integer(idMatcher.group(1));
|
||||
} else {
|
||||
imdbId = new Integer(idMatcher.group(1));
|
||||
} else
|
||||
throw new IllegalArgumentException("Cannot match imdb id: " + href);
|
||||
}
|
||||
|
||||
String yearString = XPathUtil.selectString("text()[1]", node);
|
||||
|
||||
|
@ -73,12 +72,13 @@ public class ImdbSearchEngine {
|
|||
Integer year = null;
|
||||
|
||||
if (yearMatcher.matches()) {
|
||||
year = new Integer(yearMatcher.group(1));
|
||||
} else {
|
||||
year = Integer.parseInt(yearMatcher.group(1));
|
||||
} else
|
||||
throw new IllegalArgumentException("Cannot match year: " + yearString);
|
||||
}
|
||||
|
||||
return new Movie(title, year, imdbID);
|
||||
URL imdbUrl = new URL("http", host, href);
|
||||
|
||||
return new MovieDescriptor(title, year, imdbId, imdbUrl);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
|
||||
package net.sourceforge.filebot.web;
|
||||
|
||||
|
||||
|
||||
|
||||
public class Movie {
|
||||
|
||||
private String title;
|
||||
private Integer year;
|
||||
private Integer imdbID;
|
||||
|
||||
|
||||
public Movie(String title, Integer year, Integer imdbID) {
|
||||
this.title = title;
|
||||
this.imdbID = imdbID;
|
||||
this.year = year;
|
||||
}
|
||||
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
|
||||
public Integer getImdbID() {
|
||||
return imdbID;
|
||||
}
|
||||
|
||||
|
||||
public Integer getYear() {
|
||||
return year;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (year == null)
|
||||
return title;
|
||||
|
||||
return String.format("%s (%d)", title, year);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
|
||||
package net.sourceforge.filebot.web;
|
||||
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
|
||||
public class MovieDescriptor {
|
||||
|
||||
private String title;
|
||||
private int year;
|
||||
private int imdbId;
|
||||
private URL imdbUrl;
|
||||
|
||||
|
||||
public MovieDescriptor(String title, int year, int imdbId, URL imdbUrl) {
|
||||
this.title = title;
|
||||
this.imdbId = imdbId;
|
||||
this.year = year;
|
||||
this.imdbUrl = imdbUrl;
|
||||
}
|
||||
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
|
||||
public int getYear() {
|
||||
return year;
|
||||
}
|
||||
|
||||
|
||||
public int getImdbId() {
|
||||
return imdbId;
|
||||
}
|
||||
|
||||
|
||||
public URL getImdbUrl() {
|
||||
return imdbUrl;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s (%d)", title, year);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
|
||||
package net.sourceforge.filebot.web;
|
||||
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
|
||||
/**
|
||||
* Describes a subtitle on OpenSubtitles.
|
||||
*
|
||||
* @see OpenSubtitlesClient
|
||||
*/
|
||||
public class OpenSubtitleDescriptor {
|
||||
|
||||
private Map<String, String> properties;
|
||||
|
||||
|
||||
public static enum Properties {
|
||||
IDSubMovieFile, MovieHash, MovieByteSize, MovieTimeMS, MovieFrames, IDSubtitleFile, SubFileName, SubActualCD, SubSize, SubHash, IDSubtitle, UserID, SubLanguageID, SubFormat, SubSumCD, SubAuthorComment, SubAddDate, SubBad, SubRating, SubDownloadsCnt, MovieReleaseName, IDMovie, IDMovieImdb, MovieName, MovieNameEng, MovieYear, MovieImdbRating, UserNickName, ISO639, LanguageName, SubDownloadLink, ZipDownloadLink,
|
||||
}
|
||||
|
||||
|
||||
public OpenSubtitleDescriptor(Map<String, String> properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
|
||||
public Map<String, String> getPropertyMap() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
|
||||
public String getProperty(Properties property) {
|
||||
return properties.get(property.name());
|
||||
}
|
||||
|
||||
|
||||
public long getSize() {
|
||||
return Long.parseLong(getProperty(Properties.SubSize));
|
||||
}
|
||||
|
||||
|
||||
public URL getDownloadLink() {
|
||||
String link = getProperty(Properties.SubDownloadLink);
|
||||
|
||||
try {
|
||||
return new URL(link);
|
||||
} catch (MalformedURLException e) {
|
||||
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.WARNING, "Invalid download link: " + link, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s [%s]", getProperty(Properties.SubFileName), getProperty(Properties.LanguageName));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,257 @@
|
|||
|
||||
package net.sourceforge.filebot.web;
|
||||
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import redstone.xmlrpc.XmlRpcClient;
|
||||
import redstone.xmlrpc.XmlRpcException;
|
||||
import redstone.xmlrpc.XmlRpcFault;
|
||||
|
||||
|
||||
/**
|
||||
* Client for the OpenSubtitles XML-RPC API.
|
||||
*
|
||||
*/
|
||||
public class OpenSubtitlesClient {
|
||||
|
||||
/**
|
||||
* <table>
|
||||
* <tr>
|
||||
* <td>Main server:</td>
|
||||
* <td>http://www.opensubtitles.org/xml-rpc</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>Developing tests:</td>
|
||||
* <td>http://dev.opensubtitles.org/xml-rpc</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
*/
|
||||
private String url = "http://dev.opensubtitles.org/xml-rpc";
|
||||
|
||||
private String username;
|
||||
private String password;
|
||||
private String language;
|
||||
|
||||
private String useragent;
|
||||
|
||||
private String token = null;
|
||||
|
||||
private Timer keepAliveDaemon = null;
|
||||
|
||||
/**
|
||||
* Interval to call NoOperation to keep the session from expiring
|
||||
*/
|
||||
public static final int KEEP_ALIVE_INTERVAL = 12 * 60 * 1000; // 12 minutes
|
||||
|
||||
|
||||
public OpenSubtitlesClient(String useragent) {
|
||||
this.useragent = useragent;
|
||||
}
|
||||
|
||||
|
||||
public boolean isLoggedOn() {
|
||||
return username != null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* login as anonymous user
|
||||
*/
|
||||
public synchronized void login() throws XmlRpcFault {
|
||||
this.login("", "", "en");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This will login user. This method should be called always when starting talking with
|
||||
* server.
|
||||
*
|
||||
* @param username blank for anonymous user.
|
||||
* @param password blank for anonymous user.
|
||||
* @param language <a href="http://en.wikipedia.org/wiki/List_of_ISO_639-2_codes">ISO639</a>
|
||||
* 2 letter codes as language and later communication will be done in this
|
||||
* language if applicable (error codes and so on).
|
||||
*/
|
||||
public synchronized void login(String username, String password, String language) throws XmlRpcFault {
|
||||
if (isLoggedOn())
|
||||
throw new IllegalStateException("User is already logged on");
|
||||
|
||||
if ((username == null) || (password == null) || (language == null))
|
||||
throw new IllegalArgumentException("Username, password and language must not be null");
|
||||
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.language = language;
|
||||
|
||||
activate();
|
||||
}
|
||||
|
||||
|
||||
public synchronized void logout() {
|
||||
if (!isLoggedOn())
|
||||
throw new IllegalStateException("User is not logged on");
|
||||
|
||||
deactivate();
|
||||
|
||||
username = null;
|
||||
password = null;
|
||||
language = null;
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private synchronized void activate() throws XmlRpcFault {
|
||||
if (isActive())
|
||||
return;
|
||||
|
||||
if (!isLoggedOn())
|
||||
throw new IllegalStateException("User is not logged on");
|
||||
|
||||
Map<String, String> response = (Map<String, String>) invoke("LogIn", username, password, language, useragent);
|
||||
checkStatus(response.get("status"));
|
||||
|
||||
token = response.get("token");
|
||||
|
||||
keepAliveDaemon = new Timer(getClass().getSimpleName() + " Keepalive", true);
|
||||
keepAliveDaemon.schedule(new KeepAliveTimerTask(), KEEP_ALIVE_INTERVAL, KEEP_ALIVE_INTERVAL);
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private synchronized void deactivate() {
|
||||
if (!isActive())
|
||||
return;
|
||||
|
||||
// anonymous users will always get a 401 Unauthorized when trying to logout
|
||||
if (!username.isEmpty()) {
|
||||
try {
|
||||
Map<String, String> response = (Map<String, String>) invoke("LogOut", token);
|
||||
checkStatus(response.get("status"));
|
||||
} catch (Exception e) {
|
||||
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.WARNING, "Exception while deactivating connection", e);
|
||||
}
|
||||
}
|
||||
|
||||
token = null;
|
||||
|
||||
keepAliveDaemon.cancel();
|
||||
keepAliveDaemon = null;
|
||||
}
|
||||
|
||||
|
||||
private boolean isActive() {
|
||||
return token != null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check status whether it is OK or not
|
||||
*
|
||||
* @param status status code and message (e.g. 200 OK, 401 Unauthorized, ...)
|
||||
* @throws XmlRpcFault thrown if status code is not OK
|
||||
*/
|
||||
private void checkStatus(String status) throws XmlRpcFault {
|
||||
if (status.equals("200 OK"))
|
||||
return;
|
||||
|
||||
Matcher m = Pattern.compile("(\\d+).*").matcher(status);
|
||||
|
||||
if (!m.matches())
|
||||
throw new XmlRpcException("Illegal status code: " + status);
|
||||
|
||||
throw new XmlRpcFault(Integer.parseInt(m.group(1)), status);
|
||||
}
|
||||
|
||||
|
||||
private Object invoke(String method, Object... arguments) throws XmlRpcFault {
|
||||
try {
|
||||
XmlRpcClient rpc = new XmlRpcClient(url, false);
|
||||
return rpc.invoke(method, arguments);
|
||||
} catch (MalformedURLException e) {
|
||||
// will never happen
|
||||
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.WARNING, "Invalid xml-rpc url: " + url, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This simple function returns basic server info.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Map<String, String> getServerInfo() throws XmlRpcFault {
|
||||
activate();
|
||||
|
||||
return (Map<String, String>) invoke("ServerInfo", token);
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<OpenSubtitleDescriptor> searchSubtitles(int... imdbidArray) throws XmlRpcFault {
|
||||
activate();
|
||||
|
||||
List<Map<String, String>> imdbidList = new ArrayList<Map<String, String>>(imdbidArray.length);
|
||||
|
||||
for (int imdbid : imdbidArray) {
|
||||
Map<String, String> map = new HashMap<String, String>(1);
|
||||
|
||||
// pad id with zeros
|
||||
map.put("imdbid", String.format("%07d", imdbid));
|
||||
|
||||
imdbidList.add(map);
|
||||
}
|
||||
|
||||
Map<String, List<Map<String, String>>> response = (Map<String, List<Map<String, String>>>) invoke("SearchSubtitles", token, imdbidList);
|
||||
|
||||
ArrayList<OpenSubtitleDescriptor> subs = new ArrayList<OpenSubtitleDescriptor>();
|
||||
|
||||
for (Map<String, String> subtitle : response.get("data"))
|
||||
subs.add(new OpenSubtitleDescriptor(subtitle));
|
||||
|
||||
return subs;
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean noOperation() {
|
||||
try {
|
||||
activate();
|
||||
|
||||
Map<String, String> response = (Map<String, String>) invoke("NoOperation", token);
|
||||
checkStatus(response.get("status"));
|
||||
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
deactivate();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class KeepAliveTimerTask extends TimerTask {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
|
||||
|
||||
if (!noOperation()) {
|
||||
logger.log(Level.INFO, "Connection lost");
|
||||
deactivate();
|
||||
} else {
|
||||
logger.log(Level.INFO, "Connection is OK");
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
|
||||
package net.sourceforge.filebot.web;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import net.sourceforge.tuned.XPathUtil;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Node;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
|
||||
public class SubsceneClient {
|
||||
|
||||
private Map<String, URL> cache = Collections.synchronizedMap(new TreeMap<String, URL>());
|
||||
|
||||
private String host = "subscene.com";
|
||||
|
||||
|
||||
public List<String> search(String searchterm) throws IOException, SAXException {
|
||||
|
||||
Document dom = HtmlUtil.getHtmlDocument(getSearchUrl(searchterm));
|
||||
|
||||
List<Node> nodes = XPathUtil.selectNodes("id('filmSearch')/A", dom);
|
||||
|
||||
ArrayList<String> titles = new ArrayList<String>();
|
||||
|
||||
for (Node node : nodes) {
|
||||
String title = XPathUtil.selectString("text()", node);
|
||||
String href = XPathUtil.selectString("@href", node);
|
||||
|
||||
try {
|
||||
URL url = new URL("http", host, href);
|
||||
|
||||
cache.put(title, url);
|
||||
titles.add(title);
|
||||
} catch (MalformedURLException e) {
|
||||
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.WARNING, "Invalid href: " + href, e);
|
||||
}
|
||||
}
|
||||
|
||||
return titles;
|
||||
}
|
||||
|
||||
|
||||
public List<SubsceneSubtitleDescriptor> getSubtitleList(String title) throws IOException, SAXException {
|
||||
URL url = cache.get(title);
|
||||
|
||||
if (url == null)
|
||||
throw new IllegalArgumentException("Unknown title: " + title);
|
||||
|
||||
Document dom = HtmlUtil.getHtmlDocument(url);
|
||||
|
||||
String downloadPath = XPathUtil.selectString("id('aspnetForm')/@action", dom);
|
||||
String viewstate = XPathUtil.selectString("id('__VIEWSTATE')/@value", dom);
|
||||
|
||||
List<Node> nodes = XPathUtil.selectNodes("//TABLE[@class='filmSubtitleList']//A[@id]//ancestor::TR", dom);
|
||||
|
||||
ArrayList<SubsceneSubtitleDescriptor> list = new ArrayList<SubsceneSubtitleDescriptor>();
|
||||
|
||||
for (Node node : nodes) {
|
||||
try {
|
||||
Node linkNode = XPathUtil.selectFirstNode("./TD[1]/A", node);
|
||||
|
||||
String href = XPathUtil.selectString("@href", linkNode);
|
||||
|
||||
String name = XPathUtil.selectString("./SPAN[2]", linkNode);
|
||||
String lang = XPathUtil.selectString("./SPAN[1]", linkNode);
|
||||
|
||||
int numberOfCDs = Integer.parseInt(XPathUtil.selectString("./TD[2]", node));
|
||||
String author = XPathUtil.selectString("./TD[4]", node);
|
||||
|
||||
URL downloadUrl = new URL("http", host, downloadPath);
|
||||
|
||||
Map<String, String> downloadParameters = parseParameters(href);
|
||||
downloadParameters.put("__VIEWSTATE", viewstate);
|
||||
|
||||
list.add(new SubsceneSubtitleDescriptor(name, lang, numberOfCDs, author, downloadUrl, downloadParameters));
|
||||
} catch (Exception e) {
|
||||
Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).log(Level.WARNING, "Cannot parse subtitle node", e);
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
private Map<String, String> parseParameters(String href) {
|
||||
Matcher matcher = Pattern.compile("javascript:Subtitle\\((\\d+), '(\\w+)', '0', '(\\d+)'\\);").matcher(href);
|
||||
|
||||
if (!matcher.matches())
|
||||
throw new IllegalArgumentException("Cannot extract download parameters: " + href);
|
||||
|
||||
Map<String, String> map = new HashMap<String, String>();
|
||||
|
||||
map.put("subtitleId", matcher.group(1));
|
||||
map.put("typeId", matcher.group(2));
|
||||
map.put("filmId", matcher.group(3));
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
|
||||
private URL getSearchUrl(String searchterm) throws UnsupportedEncodingException, MalformedURLException {
|
||||
String qs = URLEncoder.encode(searchterm, "UTF-8");
|
||||
String file = "/filmsearch.aspx?q=" + qs;
|
||||
return new URL("http", host, file);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
|
||||
package net.sourceforge.filebot.web;
|
||||
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URL;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
public class SubsceneSubtitleDescriptor {
|
||||
|
||||
private String title;
|
||||
private String language;
|
||||
private int numberOfCDs;
|
||||
private String author;
|
||||
|
||||
private Map<String, String> downloadParameters;
|
||||
private URL downloadUrl;
|
||||
|
||||
|
||||
public SubsceneSubtitleDescriptor(String title, String language, int numberOfCDs, String author, URL downloadUrl, Map<String, String> downloadParameters) {
|
||||
this.title = title;
|
||||
this.language = language;
|
||||
this.numberOfCDs = numberOfCDs;
|
||||
this.author = author;
|
||||
|
||||
this.downloadUrl = downloadUrl;
|
||||
this.downloadParameters = downloadParameters;
|
||||
}
|
||||
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
|
||||
public String getLanguage() {
|
||||
return language;
|
||||
}
|
||||
|
||||
|
||||
public int getNumberOfCDs() {
|
||||
return numberOfCDs;
|
||||
}
|
||||
|
||||
|
||||
public String getAuthor() {
|
||||
return author;
|
||||
}
|
||||
|
||||
|
||||
public String getArchiveType() {
|
||||
return downloadParameters.get("typeId");
|
||||
}
|
||||
|
||||
|
||||
public DownloadTask createDownloadTask() throws UnsupportedEncodingException {
|
||||
return new DownloadTask(downloadUrl, downloadParameters);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s [%s]", title, language);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
|
||||
package net.sourceforge.tuned;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
|
||||
public class ByteBufferInputStream extends InputStream {
|
||||
|
||||
private ByteBuffer buffer;
|
||||
|
||||
|
||||
public ByteBufferInputStream(ByteBuffer buffer) {
|
||||
this.buffer = buffer;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public synchronized int read() throws IOException {
|
||||
return buffer.getInt();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public synchronized int read(byte[] b, int off, int len) throws IOException {
|
||||
int length = Math.min(len, buffer.remaining());
|
||||
|
||||
buffer.get(b, off, length);
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public synchronized int available() throws IOException {
|
||||
return buffer.remaining();
|
||||
}
|
||||
|
||||
}
|
|
@ -11,7 +11,7 @@ import java.awt.GraphicsConfiguration;
|
|||
import java.awt.Insets;
|
||||
import java.awt.Point;
|
||||
import java.awt.Toolkit;
|
||||
import java.util.LinkedList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.ListIterator;
|
||||
|
||||
import javax.swing.SwingConstants;
|
||||
|
@ -23,7 +23,7 @@ import javax.swing.SwingConstants;
|
|||
*/
|
||||
public class QueueNotificationLayout implements NotificationLayout, SwingConstants {
|
||||
|
||||
private LinkedList<NotificationWindow> notificationList = new LinkedList<NotificationWindow>();
|
||||
private ArrayList<NotificationWindow> notificationList = new ArrayList<NotificationWindow>();
|
||||
|
||||
private int orientation;
|
||||
private int direction;
|
||||
|
|
Loading…
Reference in New Issue