* 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;
import static net.sourceforge.tuned.StringUtilities.*;
import java.io.File;
import java.util.AbstractCollection;
import java.util.ArrayList;
@ -24,7 +26,7 @@ public class SeriesNameMatcher {
protected final SeasonEpisodeMatcher seasonEpisodeMatcher = new SeasonEpisodeMatcher();
public String match(File file) {
return match(file.getName(), file.getParent());
}
@ -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) {
Map<File, List<File>> filesByFolder = new LinkedHashMap<File, List<File>>();
@ -294,12 +281,12 @@ public class SeriesNameMatcher {
return names;
}
protected static class SeriesNameCollection extends AbstractCollection<String> {
private final Map<String, String> data = new LinkedHashMap<String, String>();
@Override
public boolean add(String value) {
String key = value.toLowerCase();
@ -362,7 +349,7 @@ public class SeriesNameMatcher {
private final int threshold;
public ThresholdCollection(int threshold, Comparator<E> equalityComparator) {
this.heaven = new ArrayList<E>();
this.limbo = new TreeMap<E, Collection<E>>(equalityComparator);

View File

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

View File

@ -2,6 +2,8 @@
package net.sourceforge.filebot.subtitle;
import static net.sourceforge.tuned.StringUtilities.*;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@ -49,7 +51,7 @@ public class SubRipReader extends SubtitleReader {
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;
import static net.sourceforge.tuned.StringUtilities.*;
import java.text.DateFormat;
import java.util.Arrays;
import java.util.HashMap;

View File

@ -2,6 +2,8 @@
package net.sourceforge.filebot.subtitle;
import static net.sourceforge.tuned.StringUtilities.*;
import java.text.DateFormat;
import java.util.InputMismatchException;
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
public void close() throws IOException {
scanner.close();

View File

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

View File

@ -4,6 +4,9 @@ package net.sourceforge.filebot.web;
import java.net.URI;
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.logging.Level;
import java.util.logging.Logger;
@ -56,8 +59,11 @@ public class OpenSubtitlesClient implements SubtitleProvider {
// require login
login();
// singleton array with or empty array
String[] languageFilter = languageName != null ? new String[] { getSubLanguageID(languageName) } : new String[0];
@SuppressWarnings("unchecked")
List<SubtitleDescriptor> subtitles = (List) xmlrpc.searchSubtitles(((MovieDescriptor) searchResult).getImdbId(), languageName);
List<SubtitleDescriptor> subtitles = (List) xmlrpc.searchSubtitles(((MovieDescriptor) searchResult).getImdbId(), languageFilter);
return subtitles;
}
@ -99,4 +105,31 @@ public class OpenSubtitlesClient implements SubtitleProvider {
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
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 net.sourceforge.tuned.StringUtilities.*;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.InputMismatchException;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
@ -29,7 +28,7 @@ import net.sourceforge.filebot.web.OpenSubtitlesSubtitleDescriptor.Property;
*/
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;
@ -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
*
@ -126,108 +231,29 @@ public class OpenSubtitlesXmlRpc {
}
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);
private static class ParameterMap extends HashMap<String, String> {
public ParameterMap(int initialCapacity) {
super(initialCapacity);
}
}
/**
* This simple function returns basic server info.
*/
@SuppressWarnings("unchecked")
public Map<String, String> getServerInfo() throws XmlRpcFault {
return (Map<String, String>) invoke("ServerInfo", token);
}
@SuppressWarnings("unchecked")
public List<OpenSubtitlesSubtitleDescriptor> searchSubtitles(int imdbid, String languageName) throws XmlRpcFault {
Map<String, String> query = new HashMap<String, String>(2);
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)));
@Override
public String put(String key, String value) {
if (value != null && !value.isEmpty()) {
return super.put(key, value);
}
} 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;
}
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;
return null;
}
public String put(String key, Object value) {
if (value != null) {
return put(key, value.toString());
}
return null;
}
}

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