diff --git a/source/net/sourceforge/filebot/cli/CmdlineOperations.java b/source/net/sourceforge/filebot/cli/CmdlineOperations.java index b05283db..7b949bbc 100644 --- a/source/net/sourceforge/filebot/cli/CmdlineOperations.java +++ b/source/net/sourceforge/filebot/cli/CmdlineOperations.java @@ -98,7 +98,7 @@ public class CmdlineOperations implements CmdlineInterface { int cws = 0; // common word sequence double max = mediaFiles.size(); - SeriesNameMatcher nameMatcher = new SeriesNameMatcher(); + SeriesNameMatcher nameMatcher = new SeriesNameMatcher(getLenientCollator(locale)); Collection cwsList = emptySet(); if (max >= 5) { cwsList = nameMatcher.matchAll(mediaFiles.toArray(new File[0])); @@ -137,7 +137,7 @@ public class CmdlineOperations implements CmdlineInterface { List> matches = new ArrayList>(); // auto-determine optimal batch sets - for (Entry, Set> sameSeriesGroup : mapSeriesNamesByFiles(mediaFiles).entrySet()) { + for (Entry, Set> sameSeriesGroup : mapSeriesNamesByFiles(mediaFiles, locale).entrySet()) { List> batchSets = new ArrayList>(); if (sameSeriesGroup.getValue() != null && sameSeriesGroup.getValue().size() > 0) { @@ -150,7 +150,7 @@ public class CmdlineOperations implements CmdlineInterface { for (List batch : batchSets) { // auto-detect series name if not given - Collection seriesNames = (query == null) ? detectQuery(batch, strict) : singleton(query); + Collection seriesNames = (query == null) ? detectQuery(batch, locale, strict) : singleton(query); // fetch episode data Set episodes = fetchEpisodeSet(db, seriesNames, locale, strict); @@ -297,6 +297,7 @@ public class CmdlineOperations implements CmdlineInterface { // unknown hash, try via imdb id from nfo file if (movie == null) { + CLILogger.fine(format("Auto-detect movie from context: [%s]", movieFiles[i])); Collection results = detectMovie(movieFiles[i], null, service, locale, strict); movie = (Movie) selectSearchResult(query, results, strict).get(0); @@ -463,7 +464,7 @@ public class CmdlineOperations implements CmdlineInterface { // lookup subtitles via text search, only perform hash lookup in strict mode if ((query != null || !strict) && !collector.isComplete()) { // auto-detect search query - Collection querySet = (query == null) ? detectQuery(filter(files, VIDEO_FILES), false) : singleton(query); + Collection querySet = (query == null) ? detectQuery(filter(files, VIDEO_FILES), language.toLocale(), false) : singleton(query); for (SubtitleProvider service : WebServices.getSubtitleProviders()) { if (collector.isComplete()) { @@ -618,9 +619,9 @@ public class CmdlineOperations implements CmdlineInterface { } - private List detectQuery(Collection mediaFiles, boolean strict) throws Exception { + private List detectQuery(Collection mediaFiles, Locale locale, boolean strict) throws Exception { // detect series name by common word sequence - List names = detectSeriesNames(mediaFiles); + List names = detectSeriesNames(mediaFiles, locale); if (names.isEmpty() || (strict && names.size() > 1)) { throw new Exception("Unable to auto-select query: " + names); diff --git a/source/net/sourceforge/filebot/media/MediaDetection.java b/source/net/sourceforge/filebot/media/MediaDetection.java index ea289553..66adc17e 100644 --- a/source/net/sourceforge/filebot/media/MediaDetection.java +++ b/source/net/sourceforge/filebot/media/MediaDetection.java @@ -10,6 +10,7 @@ import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; +import java.text.Collator; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; @@ -24,6 +25,9 @@ import java.util.Map.Entry; import java.util.Set; import java.util.SortedMap; import java.util.TreeSet; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; @@ -42,15 +46,18 @@ import net.sourceforge.filebot.web.TheTVDBClient.TheTVDBSearchResult; public class MediaDetection { - public static Map, Set> mapSeriesNamesByFiles(Collection files) throws Exception { + private static ReleaseInfo releaseInfo = new ReleaseInfo(); + + + public static Map, Set> mapSeriesNamesByFiles(Collection files, Locale locale) throws Exception { SortedMap> filesByFolder = mapByFolder(filter(files, VIDEO_FILES, SUBTITLE_FILES)); // map series names by folder Map> seriesNamesByFolder = new HashMap>(); for (Entry> it : filesByFolder.entrySet()) { - Set namesForFolder = new TreeSet(String.CASE_INSENSITIVE_ORDER); - namesForFolder.addAll(detectSeriesNames(it.getValue())); + Set namesForFolder = new TreeSet(getLenientCollator(locale)); + namesForFolder.addAll(detectSeriesNames(it.getValue(), locale)); seriesNamesByFolder.put(it.getKey(), namesForFolder); } @@ -74,7 +81,7 @@ public class MediaDetection { Map, Set> batchSets = new HashMap, Set>(); while (seriesNamesByFolder.size() > 0) { - Set combinedNameSet = new TreeSet(String.CASE_INSENSITIVE_ORDER); + Set combinedNameSet = new TreeSet(getLenientCollator(locale)); Set combinedFolderSet = new HashSet(); // build combined match set @@ -116,12 +123,12 @@ public class MediaDetection { } - public static List detectSeriesNames(Collection files) throws Exception { + public static List detectSeriesNames(Collection files, Locale locale) throws Exception { // don't allow duplicates Map names = new LinkedHashMap(); try { - for (SearchResult it : lookupSeriesNameByInfoFile(files, Locale.ENGLISH)) { + for (SearchResult it : lookupSeriesNameByInfoFile(files, locale)) { names.put(it.getName().toLowerCase(), it.getName()); } } catch (Exception e) { @@ -129,10 +136,10 @@ public class MediaDetection { } // match common word sequence and clean detected word sequence from unwanted elements - Collection matches = new SeriesNameMatcher().matchAll(files.toArray(new File[files.size()])); + Collection matches = new SeriesNameMatcher(getLenientCollator(locale)).matchAll(files.toArray(new File[files.size()])); try { - matches = stripReleaseInfo(matches); + matches = stripReleaseInfo(matches, true); } catch (Exception e) { Logger.getLogger(MediaDetection.class.getClass().getName()).log(Level.WARNING, "Failed to clean matches: " + e.getMessage(), e); } @@ -148,6 +155,7 @@ public class MediaDetection { public static Collection detectMovie(File movieFile, MovieIdentificationService hashLookupService, MovieIdentificationService queryLookupService, Locale locale, boolean strict) throws Exception { Set options = new LinkedHashSet(); + // lookup by file hash if (hashLookupService != null) { for (Movie movie : hashLookupService.getMovieDescriptors(new File[] { movieFile }, locale)) { if (movie != null) { @@ -156,58 +164,128 @@ public class MediaDetection { } } + // lookup by id from nfo file if (queryLookupService != null) { // try to grep imdb id from nfo files for (int imdbid : grepImdbIdFor(movieFile)) { Movie movie = queryLookupService.getMovieDescriptor(imdbid, locale); - if (movie != null) { options.add(movie); } } } - if (queryLookupService != null && !strict && options.isEmpty()) { - // search by file name or folder name - Collection searchQueries = new LinkedHashSet(); - searchQueries.add(getName(movieFile)); - searchQueries.add(getName(movieFile.getParentFile())); - - // remove blacklisted terms - searchQueries = stripReleaseInfo(searchQueries); - - final SimilarityMetric metric = new NameSimilarityMetric(); - final Map probabilityMap = new LinkedHashMap(); - for (String query : searchQueries) { - for (Movie movie : queryLookupService.searchMovie(query, locale)) { - probabilityMap.put(movie, metric.getSimilarity(query, movie)); - } - } - - // sort by similarity to original query (descending) - List results = new ArrayList(probabilityMap.keySet()); - sort(results, new Comparator() { - - @Override - public int compare(Movie a, Movie b) { - return probabilityMap.get(b).compareTo(probabilityMap.get(a)); - } - }); - - options.addAll(results); + // search by file name or folder name + List files = new ArrayList(); + files.add(getName(movieFile)); + files.add(getName(movieFile.getParentFile())); + + long t = System.currentTimeMillis(); + List movieNameMatches = matchMovieName(files, locale, strict); + System.out.println(System.currentTimeMillis() - t); + + // skip further queries if collected matches are already sufficient + if (options.size() > 0 && movieNameMatches.size() > 0) { + options.addAll(movieNameMatches); + return options; + } + + // continue gathering more matches if possible + options.addAll(movieNameMatches); + + // query by file / folder name + if (queryLookupService != null && !strict) { + options.addAll(queryMovieByFileName(files, queryLookupService, locale)); } return options; } - public static String stripReleaseInfo(String name) throws IOException { - return new ReleaseInfo().cleanRelease(name); + private static List matchMovieName(final List files, final Locale locale, final boolean strict) throws Exception { + // cross-reference file / folder name with movie list + final SeriesNameMatcher nameMatcher = new SeriesNameMatcher(String.CASE_INSENSITIVE_ORDER); // use simple comparator for speed (2-3x faster) + + final Map matchMap = synchronizedMap(new HashMap()); + ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); + + for (final Movie movie : releaseInfo.getMovieList()) { + executor.submit(new Runnable() { + + @Override + public void run() { + for (String name : files) { + String movieIdentifier = movie.getName(); + String commonName = nameMatcher.matchByFirstCommonWordSequence(name, movieIdentifier); + if (commonName != null && commonName.length() >= movieIdentifier.length()) { + String strictMovieIdentifier = movie.getName() + " " + movie.getYear(); + String strictCommonName = nameMatcher.matchByFirstCommonWordSequence(name, strictMovieIdentifier); + if (strictCommonName != null && strictCommonName.length() >= strictMovieIdentifier.length()) { + // prefer strict match + matchMap.put(movie, strictCommonName); + } else if (!strict) { + // make sure the common identifier is not just the year + matchMap.put(movie, commonName); + } + } + } + } + }); + } + + // wait for last task to finish + executor.shutdown(); + executor.awaitTermination(1, TimeUnit.MINUTES); + + // sort by length of name match (descending) + List results = new ArrayList(matchMap.keySet()); + sort(results, new Comparator() { + + @Override + public int compare(Movie a, Movie b) { + return Integer.compare(matchMap.get(b).length(), matchMap.get(a).length()); + } + }); + + return results; } - public static List stripReleaseInfo(Collection names) throws IOException { - return new ReleaseInfo().cleanRelease(names); + private static Collection queryMovieByFileName(List files, MovieIdentificationService queryLookupService, Locale locale) throws Exception { + // remove blacklisted terms + Set querySet = new LinkedHashSet(); + querySet.addAll(stripReleaseInfo(files, true)); + querySet.addAll(stripReleaseInfo(files, false)); + + final SimilarityMetric metric = new NameSimilarityMetric(); + final Map probabilityMap = new LinkedHashMap(); + for (String query : querySet) { + for (Movie movie : queryLookupService.searchMovie(query, locale)) { + probabilityMap.put(movie, metric.getSimilarity(query, movie)); + } + } + + // sort by similarity to original query (descending) + List results = new ArrayList(probabilityMap.keySet()); + sort(results, new Comparator() { + + @Override + public int compare(Movie a, Movie b) { + return probabilityMap.get(b).compareTo(probabilityMap.get(a)); + } + }); + + return results; + } + + + public static String stripReleaseInfo(String name) throws IOException { + return releaseInfo.cleanRelease(name, true); + } + + + public static List stripReleaseInfo(Collection names, boolean strict) throws IOException { + return releaseInfo.cleanRelease(names, strict); } @@ -284,4 +362,13 @@ public class MediaDetection { return collection; } + + public static Comparator getLenientCollator(Locale locale) { + // use maximum strength collator by default + final Collator collator = Collator.getInstance(locale); + collator.setDecomposition(Collator.FULL_DECOMPOSITION); + collator.setStrength(Collator.TERTIARY); + + return (Comparator) collator; + } } diff --git a/source/net/sourceforge/filebot/media/ReleaseInfo.java b/source/net/sourceforge/filebot/media/ReleaseInfo.java index 5475e6b5..950266ff 100644 --- a/source/net/sourceforge/filebot/media/ReleaseInfo.java +++ b/source/net/sourceforge/filebot/media/ReleaseInfo.java @@ -4,6 +4,7 @@ package net.sourceforge.filebot.media; import static java.util.ResourceBundle.*; import static java.util.regex.Pattern.*; +import static net.sourceforge.filebot.similarity.Normalization.*; import static net.sourceforge.tuned.StringUtilities.*; import java.io.File; @@ -11,33 +12,40 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; import java.util.List; import java.util.Locale; +import java.util.Scanner; import java.util.Set; import java.util.TreeSet; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.zip.GZIPInputStream; import net.sourceforge.filebot.web.CachedResource; +import net.sourceforge.filebot.web.Movie; +import net.sourceforge.tuned.ByteBufferInputStream; public class ReleaseInfo { public String getVideoSource(File file) { // check parent and itself for group names - return matchLast(getVideoSourcePattern(), file.getParent(), file.getName()); + return matchLast(getVideoSourcePattern(), getBundle(getClass().getName()).getString("pattern.video.source").split("[|]"), file.getParent(), file.getName()); } public String getReleaseGroup(File file) throws IOException { // check parent and itself for group names - return matchLast(getReleaseGroupPattern(), file.getParent(), file.getName()); + return matchLast(getReleaseGroupPattern(false), releaseGroupResource.get(), file.getParent(), file.getName()); } - protected String matchLast(Pattern pattern, CharSequence... sequence) { + protected String matchLast(Pattern pattern, String[] standardValues, CharSequence... sequence) { String lastMatch = null; + // match last occurrence for (CharSequence name : sequence) { if (name == null) continue; @@ -48,24 +56,36 @@ public class ReleaseInfo { } } + // prefer standard value over matched value + if (lastMatch != null) { + for (String standard : standardValues) { + if (standard.equalsIgnoreCase(lastMatch)) { + return standard; + } + } + } + return lastMatch; } - public List cleanRelease(Iterable items) throws IOException { - return clean(items, getReleaseGroupPattern(), getLanguageSuffixPattern(), getVideoSourcePattern(), getVideoFormatPattern(), getResolutionPattern(), getBlacklistPattern()); + public List cleanRelease(Iterable items, boolean strict) throws IOException { + return clean(items, getReleaseGroupPattern(strict), getLanguageSuffixPattern(), getVideoSourcePattern(), getVideoFormatPattern(), getResolutionPattern(), getBlacklistPattern(false)); } - public String cleanRelease(String item) throws IOException { - return clean(item, getReleaseGroupPattern(), getLanguageSuffixPattern(), getVideoSourcePattern(), getVideoFormatPattern(), getResolutionPattern(), getBlacklistPattern()); + public String cleanRelease(String item, boolean strict) throws IOException { + return clean(item, getReleaseGroupPattern(strict), getLanguageSuffixPattern(), getVideoSourcePattern(), getVideoFormatPattern(), getResolutionPattern(), getBlacklistPattern(false)); } public List clean(Iterable items, Pattern... blacklisted) { List cleanedItems = new ArrayList(); for (String it : items) { - cleanedItems.add(clean(it, blacklisted)); + String cleanedItem = clean(it, blacklisted); + if (cleanedItem.length() > 0) { + cleanedItems.add(cleanedItem); + } } return cleanedItems; @@ -77,7 +97,7 @@ public class ReleaseInfo { item = it.matcher(item).replaceAll(""); } - return item.replaceAll("[\\p{Punct}\\p{Space}]+", " ").trim(); + return normalizePunctuation(item); } @@ -88,14 +108,16 @@ public class ReleaseInfo { Locale locale = new Locale(code); tokens.add(locale.getLanguage()); tokens.add(locale.getISO3Language()); - tokens.add(locale.getDisplayLanguage(Locale.ENGLISH)); + for (Locale language : new HashSet(Arrays.asList(Locale.ENGLISH, Locale.getDefault()))) { + tokens.add(locale.getDisplayLanguage(language)); + } } // remove illegal tokens tokens.remove(""); // .{language}[.srt] - return compile("(?<=[.])(" + join(tokens, "|") + ")(?=$)", CASE_INSENSITIVE); + return compile("(?<=\\p{Punct})(" + join(tokens, "|") + ")(?=$)", CASE_INSENSITIVE | UNICODE_CASE | CANON_EQ); } @@ -119,21 +141,27 @@ public class ReleaseInfo { } - public Pattern getReleaseGroupPattern() throws IOException { + public synchronized Pattern getReleaseGroupPattern(boolean strict) throws IOException { // pattern matching any release group name enclosed in separators - return compile("(? releaseGroupResource = new PatternResource(getBundle(getClass().getName()).getString("url.release-groups")); + protected final CachedResource queryBlacklistResource = new PatternResource(getBundle(getClass().getName()).getString("url.query-blacklist")); + protected final CachedResource movieListResource = new MovieResource(getBundle(getClass().getName()).getString("url.movie-list")); protected static class PatternResource extends CachedResource { @@ -149,4 +177,28 @@ public class ReleaseInfo { } } + + protected static class MovieResource extends CachedResource { + + public MovieResource(String resource) { + super(resource, Movie[].class, 24 * 60 * 60 * 1000); // 24h update interval + } + + + @Override + public Movie[] process(ByteBuffer data) throws IOException { + Scanner scanner = new Scanner(new GZIPInputStream(new ByteBufferInputStream(data)), "UTF-8").useDelimiter("\t|\n"); + + List movies = new ArrayList(); + while (scanner.hasNext()) { + int imdbid = scanner.nextInt(); + String name = scanner.next(); + int year = scanner.nextInt(); + movies.add(new Movie(name, year, imdbid)); + } + + return movies.toArray(new Movie[0]); + } + } + } diff --git a/source/net/sourceforge/filebot/media/ReleaseInfo.properties b/source/net/sourceforge/filebot/media/ReleaseInfo.properties index ed749392..38d0e4d0 100644 --- a/source/net/sourceforge/filebot/media/ReleaseInfo.properties +++ b/source/net/sourceforge/filebot/media/ReleaseInfo.properties @@ -2,10 +2,13 @@ pattern.video.source: CAMRip|CAM|TS|TELESYNC|PDVD|TS|TELESYNC|PDVD|PPV|PPVRip|Screener|SCR|SCREENER|DVDSCR|DVDSCREENER|BDSCR|R5|R5LINE|DVDRip|DVDR|TVRip|DSR|PDTV|HDTV|DVBRip|DTHRip|VODRip|VODR|BDRip|BRRip|BluRay|BDR|WorkPrint|VHS|VCD # additional release info patterns -pattern.video.format: DivX|Xvid|AVC|x264|h264|3ivx|mpeg|mpeg4|mp3|aac|ac3|2ch|6ch|ws|hr|720p|1080p +pattern.video.format: DivX|Xvid|AVC|x264|h264|3ivx|mpeg|mpeg4|mp3|aac|ac3|2ch|6ch|WS|HR|720p|1080p # group names mostly copied from [http://scenelingo.wordpress.com/list-of-scene-release-groups] url.release-groups: http://filebot.sourceforge.net/data/release-groups.txt # blacklisted terms that will be ignored url.query-blacklist: http://filebot.sourceforge.net/data/query-blacklist.txt + +# list of all movies (id, name, year) +url.movie-list: http://filebot.sourceforge.net/data/movies.txt.gz diff --git a/source/net/sourceforge/filebot/similarity/SeriesNameMatcher.java b/source/net/sourceforge/filebot/similarity/SeriesNameMatcher.java index 74201afc..bfdd76bb 100644 --- a/source/net/sourceforge/filebot/similarity/SeriesNameMatcher.java +++ b/source/net/sourceforge/filebot/similarity/SeriesNameMatcher.java @@ -3,6 +3,8 @@ package net.sourceforge.filebot.similarity; import static java.util.Collections.*; +import static java.util.regex.Pattern.*; +import static net.sourceforge.filebot.similarity.Normalization.*; import static net.sourceforge.tuned.StringUtilities.*; import java.io.File; @@ -28,10 +30,21 @@ import net.sourceforge.tuned.FileUtilities; public class SeriesNameMatcher { - protected final SeasonEpisodeMatcher seasonEpisodeMatcher = new SeasonEpisodeMatcher(new SeasonEpisodeFilter(30, 50, -1), true); - protected final NameSimilarityMetric nameSimilarityMetric = new NameSimilarityMetric(); + protected SeasonEpisodeMatcher seasonEpisodeMatcher = new SeasonEpisodeMatcher(new SeasonEpisodeFilter(30, 50, -1), true); + protected NameSimilarityMetric nameSimilarityMetric = new NameSimilarityMetric(); - protected final int commonWordSequenceMaxStartIndex = 3; + protected int commonWordSequenceMaxStartIndex = 3; + protected Comparator commonWordComparator; + + + public SeriesNameMatcher() { + this(String.CASE_INSENSITIVE_ORDER); + } + + + public SeriesNameMatcher(Comparator comparator) { + this.commonWordComparator = comparator; + } public Collection matchAll(File[] files) { @@ -75,7 +88,7 @@ public class SeriesNameMatcher { whitelist.addAll(deepMatchAll(focus, threshold)); // 1. use pattern matching - seriesNames.addAll(flatMatchAll(names, Pattern.compile(join(whitelist, "|"), Pattern.CASE_INSENSITIVE), threshold, false)); + seriesNames.addAll(flatMatchAll(names, compile(join(whitelist, "|"), CASE_INSENSITIVE | UNICODE_CASE | CANON_EQ), threshold, false)); // 2. use common word sequences seriesNames.addAll(whitelist); @@ -92,7 +105,7 @@ public class SeriesNameMatcher { * threshold */ private Collection flatMatchAll(String[] names, Pattern prefixPattern, int threshold, boolean strict) { - ThresholdCollection thresholdCollection = new ThresholdCollection(threshold, String.CASE_INSENSITIVE_ORDER); + ThresholdCollection thresholdCollection = new ThresholdCollection(threshold, commonWordComparator); for (String name : names) { // use normalized name @@ -191,7 +204,7 @@ public class SeriesNameMatcher { common = words; } else { // find common sequence - common = firstCommonSequence(common, words, commonWordSequenceMaxStartIndex, String.CASE_INSENSITIVE_ORDER); + common = firstCommonSequence(common, words, commonWordSequenceMaxStartIndex, commonWordComparator); if (common == null) { // no common sequence @@ -209,14 +222,12 @@ public class SeriesNameMatcher { protected String normalize(String name) { // remove group names and checksums, any [...] or (...) - name = name.replaceAll("\\([^\\(]*\\)", " "); - name = name.replaceAll("\\[[^\\[]*\\]", " "); + name = normalizeBrackets(name); // remove/normalize special characters - name = name.replaceAll("['`ยด]+", ""); - name = name.replaceAll("[\\p{Punct}\\p{Space}]+", " "); + name = normalizePunctuation(name); - return name.trim(); + return name; } diff --git a/source/net/sourceforge/filebot/ui/rename/EpisodeListMatcher.java b/source/net/sourceforge/filebot/ui/rename/EpisodeListMatcher.java index d8bb8888..0d26ea28 100644 --- a/source/net/sourceforge/filebot/ui/rename/EpisodeListMatcher.java +++ b/source/net/sourceforge/filebot/ui/rename/EpisodeListMatcher.java @@ -173,7 +173,7 @@ class EpisodeListMatcher implements AutoCompleteMatcher { List>>> taskPerFolder = new ArrayList>>>(); // detect series names and create episode list fetch tasks - for (Entry, Set> sameSeriesGroup : mapSeriesNamesByFiles(mediaFiles).entrySet()) { + for (Entry, Set> sameSeriesGroup : mapSeriesNamesByFiles(mediaFiles, locale).entrySet()) { List> batchSets = new ArrayList>(); if (sameSeriesGroup.getValue() != null && sameSeriesGroup.getValue().size() > 0) { @@ -219,7 +219,7 @@ class EpisodeListMatcher implements AutoCompleteMatcher { // detect series name and fetch episode list if (autodetection) { - Collection names = detectSeriesNames(files); + Collection names = detectSeriesNames(files, locale); if (names.size() > 0) { // only allow one fetch session at a time so later requests can make use of cached results synchronized (provider) { diff --git a/source/net/sourceforge/filebot/ui/rename/MovieHashMatcher.java b/source/net/sourceforge/filebot/ui/rename/MovieHashMatcher.java index bee8372f..ed4043e5 100644 --- a/source/net/sourceforge/filebot/ui/rename/MovieHashMatcher.java +++ b/source/net/sourceforge/filebot/ui/rename/MovieHashMatcher.java @@ -192,6 +192,7 @@ class MovieHashMatcher implements AutoCompleteMatcher { selectDialog.setTitle(movieFile.getPath()); selectDialog.getHeaderLabel().setText(String.format("Movies matching '%s':", stripReleaseInfo(getName(movieFile)))); selectDialog.getCancelAction().putValue(Action.NAME, "Ignore"); + selectDialog.pack(); // show dialog selectDialog.setLocation(getOffsetLocation(selectDialog.getOwner())); diff --git a/source/net/sourceforge/filebot/ui/subtitle/SubtitleAutoMatchDialog.java b/source/net/sourceforge/filebot/ui/subtitle/SubtitleAutoMatchDialog.java index 78887ae7..3c4760ad 100644 --- a/source/net/sourceforge/filebot/ui/subtitle/SubtitleAutoMatchDialog.java +++ b/source/net/sourceforge/filebot/ui/subtitle/SubtitleAutoMatchDialog.java @@ -26,6 +26,7 @@ import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.TreeSet; @@ -973,7 +974,7 @@ class SubtitleAutoMatchDialog extends JDialog { } // auto-detect query and search for subtitles - Collection querySet = detectSeriesNames(files); + Collection querySet = detectSeriesNames(files, Locale.ENGLISH); List subtitles = findSubtitles(service, querySet, languageName); // if auto-detection fails, ask user for input diff --git a/source/net/sourceforge/tuned/ByteBufferInputStream.java b/source/net/sourceforge/tuned/ByteBufferInputStream.java index 683e1d7d..4f03856b 100644 --- a/source/net/sourceforge/tuned/ByteBufferInputStream.java +++ b/source/net/sourceforge/tuned/ByteBufferInputStream.java @@ -11,52 +11,61 @@ public class ByteBufferInputStream extends InputStream { private final ByteBuffer buffer; - + public ByteBufferInputStream(ByteBuffer buffer) { this.buffer = buffer; } - + @Override public int read() throws IOException { - if (buffer.remaining() <= 0) - return -1; - - return buffer.get(); + return (buffer.position() < buffer.limit()) ? (buffer.get() & 0xff) : -1; } - + @Override public int read(byte[] b, int off, int len) throws IOException { - if (buffer.remaining() <= 0) + if (b == null) { + throw new NullPointerException(); + } else if (off < 0 || len < 0 || len > b.length - off) { + throw new IndexOutOfBoundsException(); + } + + if (buffer.position() >= buffer.limit()) { return -1; + } - int length = Math.min(len, buffer.remaining()); + if (len > buffer.remaining()) { + len = buffer.remaining(); + } - buffer.get(b, off, length); + if (len <= 0) { + return 0; + } - return length; + buffer.get(b, off, len); + return len; } - + @Override public int available() throws IOException { return buffer.remaining(); } - + @Override public boolean markSupported() { return true; } - + @Override public void mark(int readlimit) { buffer.mark(); } - + @Override public void reset() throws IOException { buffer.reset(); diff --git a/website/data/movies.txt.gz b/website/data/movies.txt.gz new file mode 100644 index 00000000..9ea2dd74 Binary files /dev/null and b/website/data/movies.txt.gz differ diff --git a/website/data/query-blacklist.txt b/website/data/query-blacklist.txt index 41018329..c2c7d718 100644 --- a/website/data/query-blacklist.txt +++ b/website/data/query-blacklist.txt @@ -1,10 +1,17 @@ +PROPER +RETAIL +^(TV.)?(Show|Serie|Anime)[s]?$ +^Movie[s]?$ +^Video[s]?$ CD[1-3] Demonoid ExtraScene ExtraTorrent -PROPER +Hard.Subbed +mkvonly +MVGroup.org READNFO REPACK -RETAIL ShareReactor ShareZONE +UsaBit.com diff --git a/website/data/release-groups.txt b/website/data/release-groups.txt index ef0039ea..bd2c297a 100644 --- a/website/data/release-groups.txt +++ b/website/data/release-groups.txt @@ -25,6 +25,7 @@ BAJSKORV BamHD Barba BaSS +BAUM BDiSC BiA BlueTV @@ -167,6 +168,7 @@ LMAO LoD LOL LOLCATS +LTT MAiN MainEvent MARiNES