* improved OpenSubtitles support

* refactoring
This commit is contained in:
Reinhard Pointner 2009-07-03 12:58:05 +00:00
parent 978cbe881d
commit 2fe8bd1306
11 changed files with 237 additions and 138 deletions

View File

@ -2,6 +2,8 @@
package net.sourceforge.filebot.similarity; package net.sourceforge.filebot.similarity;
import static net.sourceforge.tuned.StringUtilities.*;
import java.io.File; import java.io.File;
import java.util.AbstractCollection; import java.util.AbstractCollection;
import java.util.ArrayList; import java.util.ArrayList;
@ -239,21 +241,6 @@ public class SeriesNameMatcher {
} }
private String join(Object[] values, String separator) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < values.length; i++) {
sb.append(values[i]);
if (i < values.length - 1) {
sb.append(separator);
}
}
return sb.toString();
}
private Map<File, String[]> mapNamesByFolder(File... files) { private Map<File, String[]> mapNamesByFolder(File... files) {
Map<File, List<File>> filesByFolder = new LinkedHashMap<File, List<File>>(); Map<File, List<File>> filesByFolder = new LinkedHashMap<File, List<File>>();

View File

@ -2,6 +2,8 @@
package net.sourceforge.filebot.subtitle; package net.sourceforge.filebot.subtitle;
import static net.sourceforge.tuned.StringUtilities.*;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Scanner; import java.util.Scanner;

View File

@ -2,6 +2,8 @@
package net.sourceforge.filebot.subtitle; package net.sourceforge.filebot.subtitle;
import static net.sourceforge.tuned.StringUtilities.*;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
@ -49,7 +51,7 @@ public class SubRipReader extends SubtitleReader {
lines.add(line); lines.add(line);
} }
return new SubtitleElement(t1, t2, join(lines.toArray(), "\n")); return new SubtitleElement(t1, t2, join(lines, "\n"));
} }
} }

View File

@ -2,6 +2,8 @@
package net.sourceforge.filebot.subtitle; package net.sourceforge.filebot.subtitle;
import static net.sourceforge.tuned.StringUtilities.*;
import java.text.DateFormat; import java.text.DateFormat;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;

View File

@ -2,6 +2,8 @@
package net.sourceforge.filebot.subtitle; package net.sourceforge.filebot.subtitle;
import static net.sourceforge.tuned.StringUtilities.*;
import java.text.DateFormat; import java.text.DateFormat;
import java.util.InputMismatchException; import java.util.InputMismatchException;
import java.util.Scanner; import java.util.Scanner;

View File

@ -65,21 +65,6 @@ public abstract class SubtitleReader implements Iterator<SubtitleElement>, Close
} }
protected String join(Object[] values, String delimiter) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < values.length; i++) {
sb.append(values[i]);
if (i < values.length - 1) {
sb.append(delimiter);
}
}
return sb.toString();
}
@Override @Override
public void close() throws IOException { public void close() throws IOException {
scanner.close(); scanner.close();

View File

@ -8,6 +8,11 @@ public class MovieDescriptor extends SearchResult {
private final int imdbId; private final int imdbId;
public MovieDescriptor(String name, int imdbId) {
this(name, -1, imdbId);
}
public MovieDescriptor(String name, int year, int imdbId) { public MovieDescriptor(String name, int year, int imdbId) {
super(name); super(name);
@ -45,6 +50,9 @@ public class MovieDescriptor extends SearchResult {
@Override @Override
public String toString() { public String toString() {
if (year < 0)
return name;
return String.format("%s (%d)", name, year); return String.format("%s (%d)", name, year);
} }

View File

@ -4,6 +4,9 @@ package net.sourceforge.filebot.web;
import java.net.URI; import java.net.URI;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -56,8 +59,11 @@ public class OpenSubtitlesClient implements SubtitleProvider {
// require login // require login
login(); login();
// singleton array with or empty array
String[] languageFilter = languageName != null ? new String[] { getSubLanguageID(languageName) } : new String[0];
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
List<SubtitleDescriptor> subtitles = (List) xmlrpc.searchSubtitles(((MovieDescriptor) searchResult).getImdbId(), languageName); List<SubtitleDescriptor> subtitles = (List) xmlrpc.searchSubtitles(((MovieDescriptor) searchResult).getImdbId(), languageFilter);
return subtitles; return subtitles;
} }
@ -99,4 +105,31 @@ public class OpenSubtitlesClient implements SubtitleProvider {
logout(); logout();
} }
}; };
/**
* SubLanguageID by English language name
*/
private static final Map<String, String> subLanguageCache = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
private String getSubLanguageID(String languageName) throws Exception {
// fetch languages if necessary
synchronized (subLanguageCache) {
if (subLanguageCache.isEmpty()) {
for (Entry<String, String> entry : xmlrpc.getSubLanguages().entrySet()) {
// map id by name
subLanguageCache.put(entry.getValue(), entry.getKey());
}
}
}
String id = subLanguageCache.get(languageName);
if (id == null) {
throw new IllegalArgumentException(String.format("SubLanguageID for '%s' not found", languageName));
}
return id;
}
} }

View File

@ -85,7 +85,7 @@ public class OpenSubtitlesSubtitleDescriptor implements SubtitleDescriptor {
@Override @Override
public String getName() { public String getName() {
return getProperty(Property.SubFileName); return getProperty(Property.MovieReleaseName);
} }

View File

@ -3,15 +3,14 @@ package net.sourceforge.filebot.web;
import static java.util.Collections.*; import static java.util.Collections.*;
import static net.sourceforge.tuned.StringUtilities.*;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.InputMismatchException;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Scanner; import java.util.Scanner;
import java.util.TreeMap;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import java.util.regex.Matcher; import java.util.regex.Matcher;
@ -29,7 +28,7 @@ import net.sourceforge.filebot.web.OpenSubtitlesSubtitleDescriptor.Property;
*/ */
public class OpenSubtitlesXmlRpc { public class OpenSubtitlesXmlRpc {
private static final String url = "http://www.opensubtitles.org/xml-rpc"; private final String url = "http://www.opensubtitles.org/xml-rpc";
private final String useragent; private final String useragent;
@ -107,6 +106,112 @@ public class OpenSubtitlesXmlRpc {
} }
@SuppressWarnings("unchecked")
public Map<String, String> getServerInfo() throws XmlRpcFault {
return (Map<String, String>) invoke("ServerInfo", token);
}
public List<OpenSubtitlesSubtitleDescriptor> searchSubtitles(int imdbid, String... sublanguageids) throws XmlRpcFault {
return searchSubtitles(null, null, imdbid, sublanguageids);
}
public List<OpenSubtitlesSubtitleDescriptor> searchSubtitles(String moviehash, long moviebytesize, String... sublanguageids) throws XmlRpcFault {
return searchSubtitles(moviehash, moviebytesize, null, sublanguageids);
}
@SuppressWarnings("unchecked")
protected List<OpenSubtitlesSubtitleDescriptor> searchSubtitles(String moviehash, Long moviebytesize, Integer imdbid, String... sublanguageids) throws XmlRpcFault {
ParameterMap query = new ParameterMap(4);
// put parameters, but ignore null or empty values
query.put("moviehash", moviehash);
query.put("moviebytesize", moviebytesize);
query.put("imdbid", imdbid);
query.put("sublanguageid", join(sublanguageids, ","));
Map<String, List<Map<String, String>>> response = (Map<String, List<Map<String, String>>>) invoke("SearchSubtitles", token, singletonList(query));
List<OpenSubtitlesSubtitleDescriptor> subtitles = new ArrayList<OpenSubtitlesSubtitleDescriptor>();
try {
for (Map<String, String> subtitleData : response.get("data")) {
subtitles.add(new OpenSubtitlesSubtitleDescriptor(Property.asEnumMap(subtitleData)));
}
} catch (ClassCastException e) {
// if the response is an error message, generic types won't match
throw new XmlRpcException("Illegal response: " + response.toString());
}
return subtitles;
}
@SuppressWarnings("unchecked")
public List<MovieDescriptor> searchMoviesOnIMDB(String query) throws XmlRpcFault {
Map<String, List<Map<String, String>>> response = (Map<String, List<Map<String, String>>>) invoke("SearchMoviesOnIMDB", token, query);
List<MovieDescriptor> movies = new ArrayList<MovieDescriptor>();
for (Map<String, String> movie : response.get("data")) {
try {
// get non-aka title (aka titles are separated by Â)
Scanner titleScanner = new Scanner(movie.get("title")).useDelimiter("\u00C2");
movies.add(new MovieDescriptor(titleScanner.next(), Integer.parseInt(movie.get("id"))));
} catch (Exception e) {
Logger.getLogger(getClass().getName()).log(Level.WARNING, e.getMessage(), e);
}
}
return movies;
}
public Map<String, String> getSubLanguages() throws XmlRpcFault {
return getSubLanguages("en");
}
@SuppressWarnings("unchecked")
public Map<String, String> getSubLanguages(String languageCode) throws XmlRpcFault {
Map<String, List<Map<String, String>>> response = (Map<String, List<Map<String, String>>>) invoke("GetSubLanguages", languageCode);
Map<String, String> subLanguageMap = new HashMap<String, String>();
for (Map<String, String> language : response.get("data")) {
subLanguageMap.put(language.get("SubLanguageID"), language.get("LanguageName"));
}
return subLanguageMap;
}
@SuppressWarnings("unchecked")
public boolean noOperation() {
try {
Map<String, String> response = (Map<String, String>) invoke("NoOperation", token);
checkStatus(response.get("status"));
return true;
} catch (Exception e) {
return false;
}
}
private Object invoke(String method, Object... arguments) throws XmlRpcFault {
try {
XmlRpcClient rpc = new XmlRpcClient(url, false);
return rpc.invoke(method, arguments);
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
/** /**
* Check whether status is OK or not * Check whether status is OK or not
* *
@ -126,108 +231,29 @@ public class OpenSubtitlesXmlRpc {
} }
private Object invoke(String method, Object... arguments) throws XmlRpcFault { private static class ParameterMap extends HashMap<String, String> {
try {
XmlRpcClient rpc = new XmlRpcClient(url, false); public ParameterMap(int initialCapacity) {
return rpc.invoke(method, arguments); super(initialCapacity);
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
} }
/** @Override
* This simple function returns basic server info. public String put(String key, String value) {
*/ if (value != null && !value.isEmpty()) {
@SuppressWarnings("unchecked") return super.put(key, value);
public Map<String, String> getServerInfo() throws XmlRpcFault { }
return (Map<String, String>) invoke("ServerInfo", token);
return null;
} }
@SuppressWarnings("unchecked") public String put(String key, Object value) {
public List<OpenSubtitlesSubtitleDescriptor> searchSubtitles(int imdbid, String languageName) throws XmlRpcFault { if (value != null) {
Map<String, String> query = new HashMap<String, String>(2); return put(key, value.toString());
query.put("imdbid", String.format("%07d", imdbid));
query.put("sublanguageid", getSubLanguageID(languageName));
Map<String, List<Map<String, String>>> response = (Map<String, List<Map<String, String>>>) invoke("SearchSubtitles", token, singletonList(query));
List<OpenSubtitlesSubtitleDescriptor> subs = new ArrayList<OpenSubtitlesSubtitleDescriptor>();
try {
for (Map<String, String> subtitleData : response.get("data")) {
subs.add(new OpenSubtitlesSubtitleDescriptor(Property.asEnumMap(subtitleData)));
}
} catch (ClassCastException e) {
// if the response is an error message, generic types won't match
throw new XmlRpcException("Illegal response: " + response.toString(), e);
} }
return subs; return null;
}
private final Map<String, String> languageMap = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
@SuppressWarnings("unchecked")
public String getSubLanguageID(String languageName) throws XmlRpcFault {
synchronized (languageMap) {
if (languageMap.isEmpty()) {
Map<String, List<Map<String, String>>> response = (Map<String, List<Map<String, String>>>) invoke("GetSubLanguages", "en");
for (Map<String, String> language : response.get("data")) {
languageMap.put(language.get("LanguageName"), language.get("SubLanguageID"));
}
}
}
return languageMap.get(languageName);
}
@SuppressWarnings("unchecked")
public List<MovieDescriptor> searchMoviesOnIMDB(String query) throws XmlRpcFault {
Map<String, List<Map<String, String>>> response = (Map<String, List<Map<String, String>>>) invoke("SearchMoviesOnIMDB", token, query);
List<MovieDescriptor> movies = new ArrayList<MovieDescriptor>();
Pattern moviePattern = Pattern.compile("(.+) \\((\\d{4})\\).*");
for (Map<String, String> movie : response.get("data")) {
try {
// get non-aka title (aka titles are separated by Â)
Scanner titleScanner = new Scanner(movie.get("title")).useDelimiter("\u00C2");
Matcher matcher = moviePattern.matcher(titleScanner.next().trim());
if (!matcher.matches())
throw new InputMismatchException("Cannot parse movie: " + movie);
String title = matcher.group(1);
String year = matcher.group(2);
movies.add(new MovieDescriptor(title, Integer.parseInt(year), Integer.parseInt(movie.get("id"))));
} catch (Exception e) {
Logger.getLogger(getClass().getName()).log(Level.WARNING, e.getMessage(), e);
}
}
return movies;
}
@SuppressWarnings("unchecked")
public boolean noOperation() {
try {
Map<String, String> response = (Map<String, String>) invoke("NoOperation", token);
checkStatus(response.get("status"));
return true;
} catch (Exception e) {
return false;
} }
} }

View File

@ -0,0 +1,52 @@
package net.sourceforge.tuned;
import java.util.Iterator;
public final class StringUtilities {
public static String join(Object[] values, CharSequence delimiter) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < values.length; i++) {
sb.append(values[i]);
if (i < values.length - 1) {
sb.append(delimiter);
}
}
return sb.toString();
}
public static String join(Iterable<?> values, CharSequence separator) {
StringBuilder sb = new StringBuilder();
for (Iterator<?> iterator = values.iterator(); iterator.hasNext();) {
sb.append(iterator.next());
if (iterator.hasNext()) {
sb.append(separator);
}
}
return sb.toString();
}
public static boolean isNullOrEmpty(String value) {
return value == null || value.isEmpty();
}
/**
* Dummy constructor to prevent instantiation.
*/
private StringUtilities() {
throw new UnsupportedOperationException();
}
}