diff --git a/source/net/sourceforge/filebot/cli/CmdlineOperations.java b/source/net/sourceforge/filebot/cli/CmdlineOperations.java index 63d90b91..3d24cac8 100644 --- a/source/net/sourceforge/filebot/cli/CmdlineOperations.java +++ b/source/net/sourceforge/filebot/cli/CmdlineOperations.java @@ -41,7 +41,6 @@ import net.sourceforge.filebot.HistorySpooler; import net.sourceforge.filebot.Language; import net.sourceforge.filebot.MediaTypes; import net.sourceforge.filebot.RenameAction; -import net.sourceforge.filebot.WebServices; import net.sourceforge.filebot.archive.Archive; import net.sourceforge.filebot.archive.FileMapper; import net.sourceforge.filebot.format.ExpressionFilter; @@ -130,9 +129,9 @@ public class CmdlineOperations implements CmdlineInterface { CLILogger.finest(format("Filename pattern: [%.02f] SxE, [%.02f] CWS", sxe / max, cws / max)); if (sxe > (max * 0.65) || cws > (max * 0.65)) { - return renameSeries(files, action, conflictAction, outputDir, format, WebServices.TheTVDB, query, SortOrder.forName(sortOrder), filter, locale, strict); // use default episode db + return renameSeries(files, action, conflictAction, outputDir, format, TheTVDB, query, SortOrder.forName(sortOrder), filter, locale, strict); // use default episode db } else { - return renameMovie(files, action, conflictAction, outputDir, format, WebServices.TMDb, query, filter, locale, strict); // use default movie db + return renameMovie(files, action, conflictAction, outputDir, format, TMDb, query, filter, locale, strict); // use default movie db } } @@ -151,7 +150,7 @@ public class CmdlineOperations implements CmdlineInterface { List> matches = new ArrayList>(); // auto-determine optimal batch sets - for (Entry, Set> sameSeriesGroup : mapSeriesNamesByFiles(mediaFiles, locale).entrySet()) { + for (Entry, Set> sameSeriesGroup : mapSeriesNamesByFiles(mediaFiles, locale, db != AniDB, db == AniDB).entrySet()) { List> batchSets = new ArrayList>(); if (sameSeriesGroup.getValue() != null && sameSeriesGroup.getValue().size() > 0) { @@ -168,7 +167,7 @@ public class CmdlineOperations implements CmdlineInterface { // auto-detect series name if not given if (query == null) { // detect series name by common word sequence - seriesNames = detectSeriesNames(batch, locale); + seriesNames = detectSeriesNames(batch, locale, db != AniDB, db == AniDB); CLILogger.config("Auto-detected query: " + seriesNames); } else { // use --q option @@ -663,7 +662,7 @@ public class CmdlineOperations implements CmdlineInterface { } // lookup subtitles by hash - for (VideoHashSubtitleService service : WebServices.getVideoHashSubtitleServices()) { + for (VideoHashSubtitleService service : getVideoHashSubtitleServices()) { if (remainingVideos.isEmpty() || (databaseFilter != null && !databaseFilter.matcher(service.getName()).matches())) { continue; } @@ -687,7 +686,7 @@ public class CmdlineOperations implements CmdlineInterface { if (query == null) { try { List videoFiles = filter(files, VIDEO_FILES); - querySet.addAll(detectSeriesNames(videoFiles, language.getLocale())); + querySet.addAll(detectSeriesNames(videoFiles, language.getLocale(), true, false)); // auto-detect movie names for (File f : videoFiles) { @@ -708,7 +707,7 @@ public class CmdlineOperations implements CmdlineInterface { querySet.add(query); } - for (SubtitleProvider service : WebServices.getSubtitleProviders()) { + for (SubtitleProvider service : getSubtitleProviders()) { if (remainingVideos.isEmpty() || (databaseFilter != null && !databaseFilter.matcher(service.getName()).matches())) { continue; } diff --git a/source/net/sourceforge/filebot/media/MediaDetection.java b/source/net/sourceforge/filebot/media/MediaDetection.java index 3bc3c295..8a28d549 100644 --- a/source/net/sourceforge/filebot/media/MediaDetection.java +++ b/source/net/sourceforge/filebot/media/MediaDetection.java @@ -134,14 +134,14 @@ public class MediaDetection { return new DateMetric().parse(object); } - public static Map, Set> mapSeriesNamesByFiles(Collection files, Locale locale) throws Exception { + public static Map, Set> mapSeriesNamesByFiles(Collection files, Locale locale, boolean useSeriesIndex, boolean useAnimeIndex) throws Exception { // map series names by folder Map> seriesNamesByFolder = new HashMap>(); Map> filesByFolder = mapByFolder(files); for (Entry> it : filesByFolder.entrySet()) { Set namesForFolder = new TreeSet(getLenientCollator(locale)); - namesForFolder.addAll(detectSeriesNames(it.getValue(), locale)); + namesForFolder.addAll(detectSeriesNames(it.getValue(), locale, useSeriesIndex, useAnimeIndex)); seriesNamesByFolder.put(it.getKey(), namesForFolder); } @@ -271,6 +271,20 @@ public class MediaDetection { } public static List detectSeriesNames(Collection files, Locale locale) throws Exception { + return detectSeriesNames(files, locale, true, true); + } + + public static List detectSeriesNames(Collection files, Locale locale, boolean useSeriesIndex, boolean useAnimeIndex) throws Exception { + List> index = new ArrayList>(); + if (useSeriesIndex) + index.addAll(getSeriesIndex()); + if (useAnimeIndex) + index.addAll(getAnimeIndex()); + + return detectSeriesNames(files, locale, index); + } + + public static List detectSeriesNames(Collection files, Locale locale, List> seriesIndex) throws Exception { List names = new ArrayList(); // try xattr metadata if enabled @@ -400,16 +414,15 @@ public class MediaDetection { return matches; } - private static final List> seriesIndex = new ArrayList>(100000); + private static final ArrayList> seriesIndex = new ArrayList>(0); public static List> getSeriesIndex() throws IOException { synchronized (seriesIndex) { if (seriesIndex.isEmpty()) { + seriesIndex.ensureCapacity(100000); try { - for (SearchResult[] index : new SearchResult[][] { releaseInfo.getTheTVDBIndex(), releaseInfo.getAnidbIndex() }) { - for (SearchResult it : index) { - seriesIndex.addAll(HighPerformanceMatcher.prepare(it)); - } + for (SearchResult it : releaseInfo.getTheTVDBIndex()) { + seriesIndex.addAll(HighPerformanceMatcher.prepare(it)); } } catch (Exception e) { // can't load movie index, just try again next time @@ -423,6 +436,28 @@ public class MediaDetection { } } + private static final ArrayList> animeIndex = new ArrayList>(0); + + public static List> getAnimeIndex() throws IOException { + synchronized (animeIndex) { + if (animeIndex.isEmpty()) { + animeIndex.ensureCapacity(50000); + try { + for (SearchResult it : releaseInfo.getAnidbIndex()) { + animeIndex.addAll(HighPerformanceMatcher.prepare(it)); + } + } catch (Exception e) { + // can't load movie index, just try again next time + Logger.getLogger(MediaDetection.class.getClass().getName()).log(Level.SEVERE, "Failed to load anime index: " + e.getMessage(), e); + + // rely on online search + return emptyList(); + } + } + return animeIndex; + } + } + public static List matchSeriesByName(Collection files, int maxStartIndex) throws Exception { HighPerformanceMatcher nameMatcher = new HighPerformanceMatcher(maxStartIndex); List matches = new ArrayList(); @@ -755,11 +790,12 @@ public class MediaDetection { return matches != null && matches.size() > 0 ? matches.get(0) : null; } - private static final List> movieIndex = new ArrayList>(100000); + private static final ArrayList> movieIndex = new ArrayList>(0); public static List> getMovieIndex() throws IOException { synchronized (movieIndex) { if (movieIndex.isEmpty()) { + movieIndex.ensureCapacity(100000); try { for (Movie it : releaseInfo.getMovieList()) { movieIndex.addAll(HighPerformanceMatcher.prepare(it)); diff --git a/source/net/sourceforge/filebot/ui/rename/EpisodeListMatcher.java b/source/net/sourceforge/filebot/ui/rename/EpisodeListMatcher.java index 341f6379..72c4473e 100644 --- a/source/net/sourceforge/filebot/ui/rename/EpisodeListMatcher.java +++ b/source/net/sourceforge/filebot/ui/rename/EpisodeListMatcher.java @@ -50,11 +50,16 @@ class EpisodeListMatcher implements AutoCompleteMatcher { private final EpisodeListProvider provider; + private boolean useAnimeIndex; + private boolean useSeriesIndex; + // only allow one fetch session at a time so later requests can make use of cached results private final Object providerLock = new Object(); - public EpisodeListMatcher(EpisodeListProvider provider) { + public EpisodeListMatcher(EpisodeListProvider provider, boolean useSeriesIndex, boolean useAnimeIndex) { this.provider = provider; + this.useSeriesIndex = useSeriesIndex; + this.useAnimeIndex = useAnimeIndex; } protected SearchResult selectSearchResult(final String query, final List searchResults, Map selectionMemory, final Component parent) throws Exception { @@ -180,7 +185,7 @@ class EpisodeListMatcher implements AutoCompleteMatcher { final Map> inputMemory = new TreeMap>(CommonSequenceMatcher.getLenientCollator(Locale.ROOT)); // detect series names and create episode list fetch tasks - for (Entry, Set> sameSeriesGroup : mapSeriesNamesByFiles(mediaFiles, locale).entrySet()) { + for (Entry, Set> sameSeriesGroup : mapSeriesNamesByFiles(mediaFiles, locale, useSeriesIndex, useAnimeIndex).entrySet()) { final List> batchSets = new ArrayList>(); final Collection queries = sameSeriesGroup.getValue(); @@ -265,7 +270,7 @@ class EpisodeListMatcher implements AutoCompleteMatcher { // require user input if auto-detection has failed or has been disabled if (episodes.isEmpty()) { - List detectedSeriesNames = detectSeriesNames(files, locale); + List detectedSeriesNames = detectSeriesNames(files, locale, useSeriesIndex, useAnimeIndex); String parentPathHint = normalizePathSeparators(getRelativePathTail(files.get(0).getParentFile(), 2).getPath()); String suggestion = detectedSeriesNames.size() > 0 ? join(detectedSeriesNames, ", ") : parentPathHint; diff --git a/source/net/sourceforge/filebot/ui/rename/RenamePanel.java b/source/net/sourceforge/filebot/ui/rename/RenamePanel.java index 18d791e6..40170a08 100644 --- a/source/net/sourceforge/filebot/ui/rename/RenamePanel.java +++ b/source/net/sourceforge/filebot/ui/rename/RenamePanel.java @@ -288,8 +288,8 @@ public class RenamePanel extends JComponent { actionPopup.addDescription(new JLabel("Episode Mode:")); // create actions for match popup episode list completion - for (EpisodeListProvider provider : WebServices.getEpisodeListProviders()) { - actionPopup.add(new AutoCompleteAction(provider.getName(), provider.getIcon(), new EpisodeListMatcher(provider))); + for (EpisodeListProvider db : WebServices.getEpisodeListProviders()) { + actionPopup.add(new AutoCompleteAction(db.getName(), db.getIcon(), new EpisodeListMatcher(db, db != WebServices.AniDB, db == WebServices.AniDB))); } actionPopup.addSeparator(); diff --git a/source/net/sourceforge/filebot/ui/subtitle/SubtitleAutoMatchDialog.java b/source/net/sourceforge/filebot/ui/subtitle/SubtitleAutoMatchDialog.java index d0a6ceba..3491393e 100644 --- a/source/net/sourceforge/filebot/ui/subtitle/SubtitleAutoMatchDialog.java +++ b/source/net/sourceforge/filebot/ui/subtitle/SubtitleAutoMatchDialog.java @@ -935,7 +935,7 @@ class SubtitleAutoMatchDialog extends JDialog { Collection querySet = new TreeSet(String.CASE_INSENSITIVE_ORDER); // auto-detect series names - querySet.addAll(detectSeriesNames(files, Locale.ROOT)); + querySet.addAll(detectSeriesNames(files, Locale.ROOT, true, false)); // auto-detect movie names for (File f : files) {