diff --git a/source/net/filebot/cli/CmdlineOperations.java b/source/net/filebot/cli/CmdlineOperations.java index 1e3f739c..d6c22c66 100644 --- a/source/net/filebot/cli/CmdlineOperations.java +++ b/source/net/filebot/cli/CmdlineOperations.java @@ -718,8 +718,8 @@ public class CmdlineOperations implements CmdlineInterface { try { CLILogger.fine("Looking up subtitles by hash via " + service.getName()); - Map subtitles = lookupSubtitleByHash(service, language, remainingVideos, strict); - Map downloads = downloadSubtitleBatch(service.getName(), subtitles, outputFormat, outputEncoding, naming); + Map> options = lookupSubtitleByHash(service, language.getName(), remainingVideos, false, strict); + Map downloads = downloadSubtitleBatch(service.getName(), options, outputFormat, outputEncoding, naming); remainingVideos.removeAll(downloads.keySet()); subtitleFiles.addAll(downloads.values()); } catch (Exception e) { @@ -734,13 +734,8 @@ public class CmdlineOperations implements CmdlineInterface { try { CLILogger.fine(format("Looking up subtitles by name via %s", service.getName())); - Map subtitles = new TreeMap(); - for (Entry> it : findSubtitleMatches(service, remainingVideos, language.getName(), query, false, strict).entrySet()) { - if (it.getValue().size() > 0) { - subtitles.put(it.getKey(), it.getValue().get(0)); - } - } - Map downloads = downloadSubtitleBatch(service.getName(), subtitles, outputFormat, outputEncoding, naming); + Map> options = findSubtitleByName(service, remainingVideos, language.getName(), query, false, strict); + Map downloads = downloadSubtitleBatch(service.getName(), options, outputFormat, outputEncoding, naming); remainingVideos.removeAll(downloads.keySet()); subtitleFiles.addAll(downloads.values()); } catch (Exception e) { @@ -752,6 +747,7 @@ public class CmdlineOperations implements CmdlineInterface { for (File it : remainingVideos) { CLILogger.warning("No matching subtitles found: " + it); } + return subtitleFiles; } @@ -828,17 +824,20 @@ public class CmdlineOperations implements CmdlineInterface { } } - private Map downloadSubtitleBatch(String service, Map subtitles, SubtitleFormat outputFormat, Charset outputEncoding, SubtitleNaming naming) { - Map downloads = new HashMap(); + private Map downloadSubtitleBatch(String service, Map> subtitles, SubtitleFormat outputFormat, Charset outputEncoding, SubtitleNaming naming) { + Map downloads = new LinkedHashMap(); // fetch subtitle - for (Entry it : subtitles.entrySet()) { - try { - downloads.put(it.getKey(), downloadSubtitle(it.getValue(), it.getKey(), outputFormat, outputEncoding, naming)); - } catch (Exception e) { - CLILogger.warning(format("Failed to download %s: %s", it.getValue().getPath(), e.getMessage())); + subtitles.forEach((movie, options) -> { + if (options.size() > 0) { + SubtitleDescriptor subtitle = options.get(0); + try { + downloads.put(movie, downloadSubtitle(subtitle, movie, outputFormat, outputEncoding, naming)); + } catch (Exception e) { + CLILogger.warning(format("Failed to download %s: %s", subtitle.getPath(), e.getMessage())); + } } - } + }); return downloads; } @@ -868,22 +867,6 @@ public class CmdlineOperations implements CmdlineInterface { return destination; } - private Map lookupSubtitleByHash(VideoHashSubtitleService service, Language language, Collection videoFiles, boolean strict) throws Exception { - Map subtitleByVideo = new TreeMap(); - - for (Entry> it : service.getSubtitleList(videoFiles.toArray(new File[0]), language.getName()).entrySet()) { - // guess best hash match (default order is open bad due to invalid hash links) - SubtitleDescriptor bestMatch = getBestMatch(it.getKey(), it.getValue(), strict); - - if (bestMatch != null) { - CLILogger.finest(format("Matched [%s] to [%s] via hash", it.getKey().getName(), bestMatch.getName())); - subtitleByVideo.put(it.getKey(), bestMatch); - } - } - - return subtitleByVideo; - } - private List applyExpressionFilter(Collection input, ExpressionFilter filter) throws Exception { if (filter == null) { return new ArrayList(input); diff --git a/source/net/filebot/format/MediaBindingBean.java b/source/net/filebot/format/MediaBindingBean.java index 23b66501..eeb3710d 100644 --- a/source/net/filebot/format/MediaBindingBean.java +++ b/source/net/filebot/format/MediaBindingBean.java @@ -940,7 +940,7 @@ public class MediaBindingBean { // still no good match found -> just take the most probable video from the same folder if (videos.size() > 0) { - sort(videos, SimilarityComparator.compareTo(getMediaFile(), FileUtilities::getName)); + sort(videos, SimilarityComparator.compareTo(FileUtilities.getName(getMediaFile()), FileUtilities::getName)); return videos.get(0); } } diff --git a/source/net/filebot/similarity/SimilarityComparator.java b/source/net/filebot/similarity/SimilarityComparator.java index acd80aff..562e40ca 100644 --- a/source/net/filebot/similarity/SimilarityComparator.java +++ b/source/net/filebot/similarity/SimilarityComparator.java @@ -9,8 +9,8 @@ import java.util.function.Function; public class SimilarityComparator implements Comparator { - public static SimilarityComparator compareTo(T value, Function mapper) { - return new SimilarityComparator(new NameSimilarityMetric(), singleton(mapper.apply(value)), mapper.andThen(Collections::singleton)); + public static SimilarityComparator compareTo(S value, Function mapper) { + return new SimilarityComparator(new NameSimilarityMetric(), singleton(value), mapper.andThen(Collections::singleton)); } protected SimilarityMetric metric; diff --git a/source/net/filebot/subtitle/SubtitleUtilities.java b/source/net/filebot/subtitle/SubtitleUtilities.java index 09d31571..e49f67e8 100644 --- a/source/net/filebot/subtitle/SubtitleUtilities.java +++ b/source/net/filebot/subtitle/SubtitleUtilities.java @@ -28,6 +28,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.TreeSet; +import java.util.function.Predicate; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -41,6 +42,7 @@ import net.filebot.similarity.MetricAvg; import net.filebot.similarity.NameSimilarityMetric; import net.filebot.similarity.SeasonEpisodeMatcher.SxE; import net.filebot.similarity.SequenceMatchSimilarity; +import net.filebot.similarity.SimilarityComparator; import net.filebot.similarity.SimilarityMetric; import net.filebot.util.ByteBufferInputStream; import net.filebot.util.UnicodeReader; @@ -50,6 +52,7 @@ import net.filebot.web.Movie; import net.filebot.web.SubtitleDescriptor; import net.filebot.web.SubtitleProvider; import net.filebot.web.SubtitleSearchResult; +import net.filebot.web.VideoHashSubtitleService; import com.optimaize.langdetect.DetectedLanguage; import com.optimaize.langdetect.LanguageDetector; @@ -62,7 +65,30 @@ import com.optimaize.langdetect.profiles.LanguageProfileReader; public final class SubtitleUtilities { - public static Map> findSubtitleMatches(SubtitleProvider service, Collection fileSet, String languageName, String forceQuery, boolean addOptions, boolean strict) throws Exception { + public static Map> lookupSubtitleByHash(VideoHashSubtitleService service, String languageName, Collection files, boolean addOptions, boolean strict) throws Exception { + Map> options = service.getSubtitleList(files.toArray(new File[files.size()]), languageName); + Map> results = new LinkedHashMap>(options.size()); + + options.forEach((k, v) -> { + // guess best hash match (default order is open bad due to invalid hash links) + SubtitleDescriptor bestMatch = getBestMatch(k, v, strict); + + // ignore results if there is no best match + if (bestMatch != null) { + if (addOptions) { + Stream top1 = Stream.of(bestMatch); + Stream topN = v.stream().filter(Predicate.isEqual(bestMatch).negate()).sorted(SimilarityComparator.compareTo(getName(k), SubtitleDescriptor::getName)); + results.put(k, Stream.concat(top1, topN).collect(toList())); + } else { + results.put(k, singletonList(bestMatch)); + } + } + }); + + return results; + } + + public static Map> findSubtitleByName(SubtitleProvider service, Collection fileSet, String languageName, String forceQuery, boolean addOptions, boolean strict) throws Exception { // ignore anything that is not a video fileSet = filter(fileSet, VIDEO_FILES); @@ -331,13 +357,13 @@ public final class SubtitleUtilities { if (outputFormat == SubtitleFormat.SubRip) { // output buffer StringBuilder buffer = new StringBuilder(4 * 1024); - SubRipWriter out = new SubRipWriter(buffer); - - for (SubtitleElement it : decodeSubtitles(data)) { - if (outputTimingOffset != 0) { - it = new SubtitleElement(max(0, it.getStart() + outputTimingOffset), max(0, it.getEnd() + outputTimingOffset), it.getText()); + try (SubRipWriter out = new SubRipWriter(buffer)) { + for (SubtitleElement it : decodeSubtitles(data)) { + if (outputTimingOffset != 0) { + it = new SubtitleElement(max(0, it.getStart() + outputTimingOffset), max(0, it.getEnd() + outputTimingOffset), it.getText()); + } + out.write(it); } - out.write(it); } return outputEncoding.encode(CharBuffer.wrap(buffer)); diff --git a/source/net/filebot/ui/subtitle/SubtitleAutoMatchDialog.java b/source/net/filebot/ui/subtitle/SubtitleAutoMatchDialog.java index 9e1c1019..2d758501 100644 --- a/source/net/filebot/ui/subtitle/SubtitleAutoMatchDialog.java +++ b/source/net/filebot/ui/subtitle/SubtitleAutoMatchDialog.java @@ -915,7 +915,7 @@ class SubtitleAutoMatchDialog extends JDialog { @Override protected Map> getSubtitleList(Collection files, String languageName, Component parent) throws Exception { - return service.getSubtitleList(files.toArray(new File[0]), languageName); + return lookupSubtitleByHash(service, languageName, files, true, false); } @Override @@ -940,7 +940,7 @@ class SubtitleAutoMatchDialog extends JDialog { @Override protected Map> getSubtitleList(Collection fileSet, String languageName, Component parent) throws Exception { - return findSubtitleMatches(service, fileSet, languageName, null, true, false); + return findSubtitleByName(service, fileSet, languageName, null, true, false); } @Override