diff --git a/source/net/sourceforge/filebot/similarity/SeriesNameMatcher.java b/source/net/sourceforge/filebot/similarity/SeriesNameMatcher.java index c1c4598f..a68d751c 100644 --- a/source/net/sourceforge/filebot/similarity/SeriesNameMatcher.java +++ b/source/net/sourceforge/filebot/similarity/SeriesNameMatcher.java @@ -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 mapNamesByFolder(File... files) { Map> filesByFolder = new LinkedHashMap>(); @@ -294,12 +281,12 @@ public class SeriesNameMatcher { return names; } - + protected static class SeriesNameCollection extends AbstractCollection { private final Map data = new LinkedHashMap(); - + @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 equalityComparator) { this.heaven = new ArrayList(); this.limbo = new TreeMap>(equalityComparator); diff --git a/source/net/sourceforge/filebot/subtitle/MicroDVDReader.java b/source/net/sourceforge/filebot/subtitle/MicroDVDReader.java index 4b8bd3bd..1576851c 100644 --- a/source/net/sourceforge/filebot/subtitle/MicroDVDReader.java +++ b/source/net/sourceforge/filebot/subtitle/MicroDVDReader.java @@ -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; diff --git a/source/net/sourceforge/filebot/subtitle/SubRipReader.java b/source/net/sourceforge/filebot/subtitle/SubRipReader.java index 304d5e3f..2634caed 100644 --- a/source/net/sourceforge/filebot/subtitle/SubRipReader.java +++ b/source/net/sourceforge/filebot/subtitle/SubRipReader.java @@ -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")); } } diff --git a/source/net/sourceforge/filebot/subtitle/SubStationAlphaReader.java b/source/net/sourceforge/filebot/subtitle/SubStationAlphaReader.java index 4006ffaf..04e76bda 100644 --- a/source/net/sourceforge/filebot/subtitle/SubStationAlphaReader.java +++ b/source/net/sourceforge/filebot/subtitle/SubStationAlphaReader.java @@ -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; diff --git a/source/net/sourceforge/filebot/subtitle/SubViewerReader.java b/source/net/sourceforge/filebot/subtitle/SubViewerReader.java index 029c5b0c..cca76e27 100644 --- a/source/net/sourceforge/filebot/subtitle/SubViewerReader.java +++ b/source/net/sourceforge/filebot/subtitle/SubViewerReader.java @@ -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; diff --git a/source/net/sourceforge/filebot/subtitle/SubtitleReader.java b/source/net/sourceforge/filebot/subtitle/SubtitleReader.java index c7dd9663..29bdeda6 100644 --- a/source/net/sourceforge/filebot/subtitle/SubtitleReader.java +++ b/source/net/sourceforge/filebot/subtitle/SubtitleReader.java @@ -65,21 +65,6 @@ public abstract class SubtitleReader implements Iterator, 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(); diff --git a/source/net/sourceforge/filebot/web/MovieDescriptor.java b/source/net/sourceforge/filebot/web/MovieDescriptor.java index 23e9177a..d6effbb6 100644 --- a/source/net/sourceforge/filebot/web/MovieDescriptor.java +++ b/source/net/sourceforge/filebot/web/MovieDescriptor.java @@ -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); } diff --git a/source/net/sourceforge/filebot/web/OpenSubtitlesClient.java b/source/net/sourceforge/filebot/web/OpenSubtitlesClient.java index ce3999bb..af2599b3 100644 --- a/source/net/sourceforge/filebot/web/OpenSubtitlesClient.java +++ b/source/net/sourceforge/filebot/web/OpenSubtitlesClient.java @@ -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 subtitles = (List) xmlrpc.searchSubtitles(((MovieDescriptor) searchResult).getImdbId(), languageName); + List 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 subLanguageCache = new TreeMap(String.CASE_INSENSITIVE_ORDER); + + + private String getSubLanguageID(String languageName) throws Exception { + // fetch languages if necessary + synchronized (subLanguageCache) { + if (subLanguageCache.isEmpty()) { + for (Entry 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; + } + } diff --git a/source/net/sourceforge/filebot/web/OpenSubtitlesSubtitleDescriptor.java b/source/net/sourceforge/filebot/web/OpenSubtitlesSubtitleDescriptor.java index 940da790..e2b00143 100644 --- a/source/net/sourceforge/filebot/web/OpenSubtitlesSubtitleDescriptor.java +++ b/source/net/sourceforge/filebot/web/OpenSubtitlesSubtitleDescriptor.java @@ -85,7 +85,7 @@ public class OpenSubtitlesSubtitleDescriptor implements SubtitleDescriptor { @Override public String getName() { - return getProperty(Property.SubFileName); + return getProperty(Property.MovieReleaseName); } diff --git a/source/net/sourceforge/filebot/web/OpenSubtitlesXmlRpc.java b/source/net/sourceforge/filebot/web/OpenSubtitlesXmlRpc.java index cd67e7cb..3bc624cd 100644 --- a/source/net/sourceforge/filebot/web/OpenSubtitlesXmlRpc.java +++ b/source/net/sourceforge/filebot/web/OpenSubtitlesXmlRpc.java @@ -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 getServerInfo() throws XmlRpcFault { + return (Map) invoke("ServerInfo", token); + } + + + public List searchSubtitles(int imdbid, String... sublanguageids) throws XmlRpcFault { + return searchSubtitles(null, null, imdbid, sublanguageids); + } + + + public List searchSubtitles(String moviehash, long moviebytesize, String... sublanguageids) throws XmlRpcFault { + return searchSubtitles(moviehash, moviebytesize, null, sublanguageids); + } + + + @SuppressWarnings("unchecked") + protected List 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>> response = (Map>>) invoke("SearchSubtitles", token, singletonList(query)); + + List subtitles = new ArrayList(); + + try { + for (Map 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 searchMoviesOnIMDB(String query) throws XmlRpcFault { + Map>> response = (Map>>) invoke("SearchMoviesOnIMDB", token, query); + + List movies = new ArrayList(); + + for (Map 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 getSubLanguages() throws XmlRpcFault { + return getSubLanguages("en"); + } + + + @SuppressWarnings("unchecked") + public Map getSubLanguages(String languageCode) throws XmlRpcFault { + Map>> response = (Map>>) invoke("GetSubLanguages", languageCode); + + Map subLanguageMap = new HashMap(); + + for (Map language : response.get("data")) { + subLanguageMap.put(language.get("SubLanguageID"), language.get("LanguageName")); + } + + return subLanguageMap; + } + + + @SuppressWarnings("unchecked") + public boolean noOperation() { + try { + Map response = (Map) 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 { + + public ParameterMap(int initialCapacity) { + super(initialCapacity); } - } - + - /** - * This simple function returns basic server info. - */ - @SuppressWarnings("unchecked") - public Map getServerInfo() throws XmlRpcFault { - return (Map) invoke("ServerInfo", token); - } - - - @SuppressWarnings("unchecked") - public List searchSubtitles(int imdbid, String languageName) throws XmlRpcFault { - Map query = new HashMap(2); - - query.put("imdbid", String.format("%07d", imdbid)); - query.put("sublanguageid", getSubLanguageID(languageName)); - - Map>> response = (Map>>) invoke("SearchSubtitles", token, singletonList(query)); - - List subs = new ArrayList(); - - try { - for (Map 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 languageMap = new TreeMap(String.CASE_INSENSITIVE_ORDER); - - - @SuppressWarnings("unchecked") - public String getSubLanguageID(String languageName) throws XmlRpcFault { - synchronized (languageMap) { - if (languageMap.isEmpty()) { - Map>> response = (Map>>) invoke("GetSubLanguages", "en"); - - for (Map language : response.get("data")) { - languageMap.put(language.get("LanguageName"), language.get("SubLanguageID")); - } - } - } - - return languageMap.get(languageName); - } - - - @SuppressWarnings("unchecked") - public List searchMoviesOnIMDB(String query) throws XmlRpcFault { - - Map>> response = (Map>>) invoke("SearchMoviesOnIMDB", token, query); - - List movies = new ArrayList(); - Pattern moviePattern = Pattern.compile("(.+) \\((\\d{4})\\).*"); - - for (Map 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 response = (Map) 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; } } diff --git a/source/net/sourceforge/tuned/StringUtilities.java b/source/net/sourceforge/tuned/StringUtilities.java new file mode 100644 index 00000000..0988c884 --- /dev/null +++ b/source/net/sourceforge/tuned/StringUtilities.java @@ -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(); + } + +}