* support OpenSubtitles TAG lookup (i.e. filename) as part of hash lookup if actual hash lookup does not yield any results

This commit is contained in:
Reinhard Pointner 2014-01-27 08:36:30 +00:00
parent 6e732e8987
commit 5fadfbe3e3
4 changed files with 168 additions and 80 deletions

View File

@ -17,6 +17,7 @@ import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@ -134,6 +135,29 @@ public class OpenSubtitlesClient implements SubtitleProvider, VideoHashSubtitleS
@Override @Override
public Map<File, List<SubtitleDescriptor>> getSubtitleList(File[] files, String languageName) throws Exception { public Map<File, List<SubtitleDescriptor>> getSubtitleList(File[] files, String languageName) throws Exception {
Map<File, List<SubtitleDescriptor>> results = new HashMap<File, List<SubtitleDescriptor>>(files.length);
Set<File> remainingFiles = new LinkedHashSet<File>(asList(files));
// lookup subtitles by hash
if (remainingFiles.size() > 0) {
results.putAll(getSubtitleListByHash(remainingFiles.toArray(new File[0]), languageName));
}
for (Entry<File, List<SubtitleDescriptor>> it : results.entrySet()) {
if (it.getValue().size() > 0) {
remainingFiles.remove(it.getKey());
}
}
// lookup subtitles by tag
if (remainingFiles.size() > 0) {
results.putAll(getSubtitleListByTag(remainingFiles.toArray(new File[0]), languageName));
}
return results;
}
public Map<File, List<SubtitleDescriptor>> getSubtitleListByHash(File[] files, String languageName) throws Exception {
// singleton array with or empty array // singleton array with or empty array
String[] languageFilter = languageName != null ? new String[] { getSubLanguageID(languageName) } : new String[0]; String[] languageFilter = languageName != null ? new String[] { getSubLanguageID(languageName) } : new String[0];
@ -142,10 +166,10 @@ public class OpenSubtitlesClient implements SubtitleProvider, VideoHashSubtitleS
Map<File, List<SubtitleDescriptor>> resultMap = new HashMap<File, List<SubtitleDescriptor>>(files.length); Map<File, List<SubtitleDescriptor>> resultMap = new HashMap<File, List<SubtitleDescriptor>>(files.length);
// create hash query for each file // create hash query for each file
List<Query> queryList = new ArrayList<Query>(files.length); List<Query> hashQueryList = new ArrayList<Query>(files.length);
for (File file : files) { for (File file : files) {
// add query // add hash query
if (file.length() > HASH_CHUNK_SIZE) { if (file.length() > HASH_CHUNK_SIZE) {
String movieHash = computeHash(file); String movieHash = computeHash(file);
Query query = Query.forHash(movieHash, file.length(), languageFilter); Query query = Query.forHash(movieHash, file.length(), languageFilter);
@ -153,7 +177,7 @@ public class OpenSubtitlesClient implements SubtitleProvider, VideoHashSubtitleS
// check hash // check hash
List<SubtitleDescriptor> cachedResults = getCache().getSubtitleDescriptorList(query, languageName); List<SubtitleDescriptor> cachedResults = getCache().getSubtitleDescriptorList(query, languageName);
if (cachedResults == null) { if (cachedResults == null) {
queryList.add(query); hashQueryList.add(query);
hashMap.put(query, file); hashMap.put(query, file);
} else { } else {
resultMap.put(file, cachedResults); resultMap.put(file, cachedResults);
@ -166,22 +190,26 @@ public class OpenSubtitlesClient implements SubtitleProvider, VideoHashSubtitleS
} }
} }
if (queryList.size() > 0) { if (hashQueryList.size() > 0) {
// require login // require login
login(); login();
// dispatch query for all hashes // dispatch query for all hashes
int batchSize = 50; int batchSize = 50;
for (int bn = 0; bn < ceil((float) queryList.size() / batchSize); bn++) { for (int bn = 0; bn < ceil((float) hashQueryList.size() / batchSize); bn++) {
List<Query> batch = queryList.subList(bn * batchSize, min((bn * batchSize) + batchSize, queryList.size())); List<Query> batch = hashQueryList.subList(bn * batchSize, min((bn * batchSize) + batchSize, hashQueryList.size()));
// submit query and map results to given files // submit query and map results to given files
for (OpenSubtitlesSubtitleDescriptor subtitle : xmlrpc.searchSubtitles(batch)) { for (OpenSubtitlesSubtitleDescriptor subtitle : xmlrpc.searchSubtitles(batch)) {
// get file for hash // get file for hash
File file = hashMap.get(Query.forHash(subtitle.getMovieHash(), subtitle.getMovieByteSize(), languageFilter)); File file = hashMap.get((batch.get(subtitle.getQueryNumber())));
// add subtitle // add subtitle
resultMap.get(file).add(subtitle); if (file != null) {
resultMap.get(file).add(subtitle);
} else {
Logger.getLogger(getClass().getName()).log(Level.WARNING, "Unable to map hash to file: " + subtitle.getMovieHash());
}
} }
for (Query query : batch) { for (Query query : batch) {
@ -193,6 +221,68 @@ public class OpenSubtitlesClient implements SubtitleProvider, VideoHashSubtitleS
return resultMap; return resultMap;
} }
public Map<File, List<SubtitleDescriptor>> getSubtitleListByTag(File[] files, String languageName) throws Exception {
// singleton array with or empty array
String[] languageFilter = languageName != null ? new String[] { getSubLanguageID(languageName) } : new String[0];
// remember tag for each file
Map<Query, File> tagMap = new HashMap<Query, File>(files.length);
Map<File, List<SubtitleDescriptor>> resultMap = new HashMap<File, List<SubtitleDescriptor>>(files.length);
// create tag query for each file
List<Query> tagQueryList = new ArrayList<Query>(files.length);
for (File file : files) {
// add tag query
String tag = getNameWithoutExtension(file.getName());
Query query = Query.forTag(tag, languageFilter);
// check tag
List<SubtitleDescriptor> cachedResults = getCache().getSubtitleDescriptorList(query, languageName);
if (cachedResults == null) {
tagQueryList.add(query);
tagMap.put(query, file);
} else {
resultMap.put(file, cachedResults);
}
// prepare result map
if (resultMap.get(file) == null) {
resultMap.put(file, new LinkedList<SubtitleDescriptor>());
}
}
if (tagQueryList.size() > 0) {
// require login
login();
// dispatch query for all hashes
int batchSize = 50;
for (int bn = 0; bn < ceil((float) tagQueryList.size() / batchSize); bn++) {
List<Query> batch = tagQueryList.subList(bn * batchSize, min((bn * batchSize) + batchSize, tagQueryList.size()));
// submit query and map results to given files
for (OpenSubtitlesSubtitleDescriptor subtitle : xmlrpc.searchSubtitles(batch)) {
// get file for tag
File file = tagMap.get(batch.get(subtitle.getQueryNumber()));
// add subtitle
if (file != null) {
resultMap.get(file).add(subtitle);
} else {
Logger.getLogger(getClass().getName()).log(Level.WARNING, "Unable to map release name to file: " + subtitle.getMovieReleaseName());
}
}
for (Query query : batch) {
getCache().putSubtitleDescriptorList(query, languageName, resultMap.get(tagMap.get(query)));
}
}
}
return resultMap;
}
@Override @Override
public CheckResult checkSubtitle(File videoFile, File subtitleFile) throws Exception { public CheckResult checkSubtitle(File videoFile, File subtitleFile) throws Exception {
// subhash (md5 of subtitles), subfilename, moviehash, moviebytesize, moviefilename // subhash (md5 of subtitles), subfilename, moviehash, moviebytesize, moviefilename

View File

@ -23,7 +23,7 @@ import net.sourceforge.tuned.FileUtilities;
public class OpenSubtitlesSubtitleDescriptor implements SubtitleDescriptor, Serializable { public class OpenSubtitlesSubtitleDescriptor implements SubtitleDescriptor, Serializable {
public static enum Property { public static enum Property {
IDSubtitle, IDSubtitleFile, IDSubMovieFile, IDMovie, IDMovieImdb, SubFileName, SubFormat, SubHash, SubSize, MovieHash, MovieByteSize, MovieName, MovieNameEng, MovieYear, MovieReleaseName, MovieTimeMS, MovieFPS, MovieImdbRating, MovieKind, SeriesSeason, SeriesEpisode, SeriesIMDBParent, SubLanguageID, ISO639, LanguageName, UserID, UserRank, UserNickName, SubAddDate, SubAuthorComment, SubFeatured, SubComments, SubDownloadsCnt, SubHearingImpaired, SubRating, SubHD, SubBad, SubActualCD, SubSumCD, MatchedBy, SubtitlesLink, SubDownloadLink, ZipDownloadLink; IDSubtitle, IDSubtitleFile, IDSubMovieFile, IDMovie, IDMovieImdb, SubFileName, SubFormat, SubHash, SubSize, MovieHash, MovieByteSize, MovieName, MovieNameEng, MovieYear, MovieReleaseName, MovieTimeMS, MovieFPS, MovieImdbRating, MovieKind, SeriesSeason, SeriesEpisode, SeriesIMDBParent, SubLanguageID, ISO639, LanguageName, UserID, UserRank, UserNickName, SubAddDate, SubAuthorComment, SubFeatured, SubComments, SubDownloadsCnt, SubHearingImpaired, SubRating, SubHD, SubBad, SubActualCD, SubSumCD, MatchedBy, QueryNumber, SubtitlesLink, SubDownloadLink, ZipDownloadLink;
public static <V> EnumMap<Property, V> asEnumMap(Map<String, V> stringMap) { public static <V> EnumMap<Property, V> asEnumMap(Map<String, V> stringMap) {
EnumMap<Property, V> enumMap = new EnumMap<Property, V>(Property.class); EnumMap<Property, V> enumMap = new EnumMap<Property, V>(Property.class);
@ -47,6 +47,10 @@ public class OpenSubtitlesSubtitleDescriptor implements SubtitleDescriptor, Seri
this.properties = properties; this.properties = properties;
} }
public Map<Property, String> getProperties() {
return properties;
}
public String getProperty(Property key) { public String getProperty(Property key) {
return properties.get(key); return properties.get(key);
} }
@ -84,6 +88,14 @@ public class OpenSubtitlesSubtitleDescriptor implements SubtitleDescriptor, Seri
return Long.parseLong(getProperty(Property.MovieByteSize)); return Long.parseLong(getProperty(Property.MovieByteSize));
} }
public String getMovieReleaseName() {
return getProperty(Property.MovieReleaseName);
}
public int getQueryNumber() {
return Integer.parseInt(getProperty(Property.QueryNumber));
}
@Override @Override
public ByteBuffer fetch() throws Exception { public ByteBuffer fetch() throws Exception {
URL resource = new URL(getProperty(Property.SubDownloadLink)); URL resource = new URL(getProperty(Property.SubDownloadLink));

View File

@ -377,23 +377,27 @@ public class OpenSubtitlesXmlRpc {
public static final class Query extends HashMap<String, Object> implements Serializable { public static final class Query extends HashMap<String, Object> implements Serializable {
private Query(String imdbid, String... sublanguageids) { private Query(String... sublanguageids) {
put("imdbid", imdbid);
put("sublanguageid", join(sublanguageids, ","));
}
private Query(String moviehash, String moviebytesize, String... sublanguageids) {
put("moviehash", moviehash);
put("moviebytesize", moviebytesize);
put("sublanguageid", join(sublanguageids, ",")); put("sublanguageid", join(sublanguageids, ","));
} }
public static Query forHash(String moviehash, long moviebytesize, String... sublanguageids) { public static Query forHash(String moviehash, long moviebytesize, String... sublanguageids) {
return new Query(moviehash, Long.toString(moviebytesize), sublanguageids); Query query = new Query(sublanguageids);
query.put("moviehash", moviehash);
query.put("moviebytesize", Long.toString(moviebytesize));
return query;
}
public static Query forTag(String tag, String... sublanguageids) {
Query query = new Query(sublanguageids);
query.put("tag", tag);
return query;
} }
public static Query forImdbId(int imdbid, String... sublanguageids) { public static Query forImdbId(int imdbid, String... sublanguageids) {
return new Query(Integer.toString(imdbid), sublanguageids); Query query = new Query(sublanguageids);
query.put("imdbid", Integer.toString(imdbid));
return query;
} }
} }

View File

@ -1,7 +1,5 @@
package net.sourceforge.filebot.web; package net.sourceforge.filebot.web;
import static java.util.Collections.*; import static java.util.Collections.*;
import static net.sourceforge.filebot.Settings.*; import static net.sourceforge.filebot.Settings.*;
import static org.junit.Assert.*; import static org.junit.Assert.*;
@ -19,19 +17,16 @@ import org.junit.AfterClass;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
public class OpenSubtitlesXmlRpcTest { public class OpenSubtitlesXmlRpcTest {
private static OpenSubtitlesXmlRpc xmlrpc = new OpenSubtitlesXmlRpc(String.format("%s %s", getApplicationName(), getApplicationVersion())); private static OpenSubtitlesXmlRpc xmlrpc = new OpenSubtitlesXmlRpc(String.format("%s %s", getApplicationName(), getApplicationVersion()));
@BeforeClass @BeforeClass
public static void login() throws Exception { public static void login() throws Exception {
// login manually // login manually
xmlrpc.loginAnonymous(); xmlrpc.loginAnonymous();
} }
@Test @Test
public void search() throws Exception { public void search() throws Exception {
List<Movie> list = xmlrpc.searchMoviesOnIMDB("babylon 5"); List<Movie> list = xmlrpc.searchMoviesOnIMDB("babylon 5");
@ -43,16 +38,15 @@ public class OpenSubtitlesXmlRpcTest {
assertEquals(105946, sample.getImdbId()); assertEquals(105946, sample.getImdbId());
} }
@Test(expected = IndexOutOfBoundsException.class) @Test(expected = IndexOutOfBoundsException.class)
public void searchOST() throws Exception { public void searchOST() throws Exception {
List<Movie> list = xmlrpc.searchMoviesOnIMDB("Linkin.Park.New.Divide.1280-720p.Transformers.Revenge.of.the.Fallen.ost"); List<Movie> list = xmlrpc.searchMoviesOnIMDB("Linkin.Park.New.Divide.1280-720p.Transformers.Revenge.of.the.Fallen.ost");
// seek to OST entry, expect to fail // seek to OST entry, expect to fail
for (int i = 0; !list.get(i).getName().contains("Linkin.Park"); i++); for (int i = 0; !list.get(i).getName().contains("Linkin.Park"); i++)
;
} }
@Test @Test
public void getSubtitleListEnglish() throws Exception { public void getSubtitleListEnglish() throws Exception {
List<OpenSubtitlesSubtitleDescriptor> list = xmlrpc.searchSubtitles(361256, "eng"); List<OpenSubtitlesSubtitleDescriptor> list = xmlrpc.searchSubtitles(361256, "eng");
@ -66,7 +60,6 @@ public class OpenSubtitlesXmlRpcTest {
assertTrue(list.size() > 20); assertTrue(list.size() > 20);
} }
@Test @Test
public void getSubtitleListAllLanguages() throws Exception { public void getSubtitleListAllLanguages() throws Exception {
List<OpenSubtitlesSubtitleDescriptor> list = xmlrpc.searchSubtitles(361256); List<OpenSubtitlesSubtitleDescriptor> list = xmlrpc.searchSubtitles(361256);
@ -81,7 +74,6 @@ public class OpenSubtitlesXmlRpcTest {
assertTrue(list.size() > 70); assertTrue(list.size() > 70);
} }
@Test @Test
public void getSubtitleListMovieHash() throws Exception { public void getSubtitleListMovieHash() throws Exception {
List<OpenSubtitlesSubtitleDescriptor> list = xmlrpc.searchSubtitles(singleton(Query.forHash("2bba5c34b007153b", 717565952, "eng"))); List<OpenSubtitlesSubtitleDescriptor> list = xmlrpc.searchSubtitles(singleton(Query.forHash("2bba5c34b007153b", 717565952, "eng")));
@ -93,7 +85,6 @@ public class OpenSubtitlesXmlRpcTest {
assertEquals("moviehash", sample.getProperty(Property.MatchedBy)); assertEquals("moviehash", sample.getProperty(Property.MatchedBy));
} }
@Test @Test
public void tryUploadSubtitles() throws Exception { public void tryUploadSubtitles() throws Exception {
SubFile subtitle = new SubFile(); SubFile subtitle = new SubFile();
@ -110,7 +101,6 @@ public class OpenSubtitlesXmlRpcTest {
assertEquals("eng", response.getSubtitleData().get(0).get(Property.SubLanguageID.toString())); assertEquals("eng", response.getSubtitleData().get(0).get(Property.SubLanguageID.toString()));
} }
@Test @Test
public void checkSubHash() throws Exception { public void checkSubHash() throws Exception {
Map<String, Integer> subHashMap = xmlrpc.checkSubHash(singleton("e12715f466ee73c86694b7ab9f311285")); Map<String, Integer> subHashMap = xmlrpc.checkSubHash(singleton("e12715f466ee73c86694b7ab9f311285"));
@ -119,7 +109,6 @@ public class OpenSubtitlesXmlRpcTest {
assertTrue(1 == subHashMap.size()); assertTrue(1 == subHashMap.size());
} }
@Test @Test
public void checkSubHashInvalid() throws Exception { public void checkSubHashInvalid() throws Exception {
Map<String, Integer> subHashMap = xmlrpc.checkSubHash(singleton("0123456789abcdef0123456789abcdef")); Map<String, Integer> subHashMap = xmlrpc.checkSubHash(singleton("0123456789abcdef0123456789abcdef"));
@ -128,7 +117,6 @@ public class OpenSubtitlesXmlRpcTest {
assertTrue(1 == subHashMap.size()); assertTrue(1 == subHashMap.size());
} }
@Test @Test
public void checkMovieHash() throws Exception { public void checkMovieHash() throws Exception {
Map<String, Movie> results = xmlrpc.checkMovieHash(singleton("d7aa0275cace4410"), 0); Map<String, Movie> results = xmlrpc.checkMovieHash(singleton("d7aa0275cace4410"), 0);
@ -139,7 +127,6 @@ public class OpenSubtitlesXmlRpcTest {
assertEquals(371746, movie.getImdbId()); assertEquals(371746, movie.getImdbId());
} }
@Test @Test
public void checkMovieHashInvalid() throws Exception { public void checkMovieHashInvalid() throws Exception {
Map<String, Movie> results = xmlrpc.checkMovieHash(singleton("0123456789abcdef"), 0); Map<String, Movie> results = xmlrpc.checkMovieHash(singleton("0123456789abcdef"), 0);
@ -148,7 +135,6 @@ public class OpenSubtitlesXmlRpcTest {
assertTrue(results.isEmpty()); assertTrue(results.isEmpty());
} }
@Test @Test
public void getIMDBMovieDetails() throws Exception { public void getIMDBMovieDetails() throws Exception {
Movie movie = xmlrpc.getIMDBMovieDetails(371746); Movie movie = xmlrpc.getIMDBMovieDetails(371746);
@ -158,7 +144,6 @@ public class OpenSubtitlesXmlRpcTest {
assertEquals(371746, movie.getImdbId()); assertEquals(371746, movie.getImdbId());
} }
@Test @Test
public void getIMDBMovieDetailsInvalid() throws Exception { public void getIMDBMovieDetailsInvalid() throws Exception {
Movie movie = xmlrpc.getIMDBMovieDetails(371746); Movie movie = xmlrpc.getIMDBMovieDetails(371746);
@ -168,7 +153,6 @@ public class OpenSubtitlesXmlRpcTest {
assertEquals(371746, movie.getImdbId()); assertEquals(371746, movie.getImdbId());
} }
@Test @Test
public void detectLanguage() throws Exception { public void detectLanguage() throws Exception {
String text = "Only those that are prepared to fire should be fired at."; String text = "Only those that are prepared to fire should be fired at.";
@ -179,7 +163,6 @@ public class OpenSubtitlesXmlRpcTest {
assertTrue(1 == languages.size()); assertTrue(1 == languages.size());
} }
@Test @Test
public void fetchSubtitle() throws Exception { public void fetchSubtitle() throws Exception {
List<OpenSubtitlesSubtitleDescriptor> list = xmlrpc.searchSubtitles(361256, "eng"); List<OpenSubtitlesSubtitleDescriptor> list = xmlrpc.searchSubtitles(361256, "eng");
@ -194,7 +177,6 @@ public class OpenSubtitlesXmlRpcTest {
assertEquals(48717, data.remaining(), 0); assertEquals(48717, data.remaining(), 0);
} }
@AfterClass @AfterClass
public static void logout() throws Exception { public static void logout() throws Exception {
// logout manually // logout manually