1
0
mirror of https://github.com/mitb-archive/filebot synced 2025-03-10 06:20:27 -04:00

* unify GUI/CLI probable match selection

This commit is contained in:
Reinhard Pointner 2016-01-23 22:25:05 +00:00
parent 8de14a1b71
commit 40a98b08ae
4 changed files with 44 additions and 56 deletions

View File

@ -162,7 +162,8 @@ public final class WebServices {
} }
} }
} }
return sortBySimilarity(results, singleton(query), getSeriesMatchMetric(), false);
return sortBySimilarity(results, singleton(query), getSeriesMatchMetric());
} }
} }
@ -209,7 +210,7 @@ public final class WebServices {
public synchronized List<SubtitleSearchResult> search(final String query) throws Exception { public synchronized List<SubtitleSearchResult> search(final String query) throws Exception {
List<SubtitleSearchResult> results = getLocalIndex().search(query); List<SubtitleSearchResult> results = getLocalIndex().search(query);
return sortBySimilarity(results, singleton(query), new MetricAvg(getSeriesMatchMetric(), getMovieMatchMetric()), false); return sortBySimilarity(results, singleton(query), new MetricAvg(getSeriesMatchMetric(), getMovieMatchMetric()));
} }
} }

View File

@ -55,10 +55,7 @@ import net.filebot.media.XattrMetaInfoProvider;
import net.filebot.similarity.CommonSequenceMatcher; import net.filebot.similarity.CommonSequenceMatcher;
import net.filebot.similarity.EpisodeMatcher; import net.filebot.similarity.EpisodeMatcher;
import net.filebot.similarity.Match; import net.filebot.similarity.Match;
import net.filebot.similarity.NameSimilarityMetric;
import net.filebot.similarity.SeriesNameMatcher; import net.filebot.similarity.SeriesNameMatcher;
import net.filebot.similarity.SimilarityComparator;
import net.filebot.similarity.SimilarityMetric;
import net.filebot.subtitle.SubtitleFormat; import net.filebot.subtitle.SubtitleFormat;
import net.filebot.subtitle.SubtitleNaming; import net.filebot.subtitle.SubtitleNaming;
import net.filebot.util.EntryList; import net.filebot.util.EntryList;
@ -908,36 +905,9 @@ public class CmdlineOperations implements CmdlineInterface {
return output; return output;
} }
public List<SearchResult> findProbableMatches(final String query, Collection<? extends SearchResult> searchResults, boolean strict) {
if (query == null) {
return new ArrayList<SearchResult>(searchResults);
}
// auto-select most probable search result
List<SearchResult> probableMatches = new ArrayList<SearchResult>();
// use name similarity metric
SimilarityMetric metric = new NameSimilarityMetric();
// find probable matches using name similarity > 0.8 (or > 0.6 in non-strict mode)
for (SearchResult result : searchResults) {
float f = metric.getSimilarity(query, result.getName());
if (f >= (strict && searchResults.size() > 1 ? 0.8 : 0.6) || ((f >= 0.5 || !strict) && (result.getName().toLowerCase().startsWith(query.toLowerCase())))) {
if (!probableMatches.contains(result)) {
probableMatches.add(result);
}
}
}
// sort results by similarity to query
sort(probableMatches, new SimilarityComparator(query));
return probableMatches;
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public List<SearchResult> selectSearchResult(String query, Collection<? extends SearchResult> searchResults, boolean strict) throws Exception { public List<SearchResult> selectSearchResult(String query, Collection<? extends SearchResult> searchResults, boolean strict) throws Exception {
List<SearchResult> probableMatches = findProbableMatches(query, searchResults, strict); List<SearchResult> probableMatches = getProbableMatches(query, searchResults, strict);
if (probableMatches.isEmpty() || (strict && probableMatches.size() != 1)) { if (probableMatches.isEmpty() || (strict && probableMatches.size() != 1)) {
// allow single search results to just pass through in non-strict mode even if match confidence is low // allow single search results to just pass through in non-strict mode even if match confidence is low
@ -951,7 +921,7 @@ public class CmdlineOperations implements CmdlineInterface {
// just pick the best 5 matches // just pick the best 5 matches
if (query != null) { if (query != null) {
probableMatches = (List<SearchResult>) sortBySimilarity(searchResults, singleton(query), getSeriesMatchMetric(), false); probableMatches = (List<SearchResult>) sortBySimilarity(searchResults, singleton(query), getSeriesMatchMetric());
} }
} }

View File

@ -714,19 +714,9 @@ public class MediaDetection {
return new MetricAvg(new SequenceMatchSimilarity(), new NameSimilarityMetric(), new SequenceMatchSimilarity(0, true)); return new MetricAvg(new SequenceMatchSimilarity(), new NameSimilarityMetric(), new SequenceMatchSimilarity(0, true));
} }
public static <T> List<T> sortBySimilarity(Collection<T> options, Collection<String> terms, SimilarityMetric metric, boolean stripReleaseInfo) throws IOException { public static <T> List<T> sortBySimilarity(Collection<T> options, Collection<String> terms, SimilarityMetric metric) {
Collection<String> paragon = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
// clean clutter tokens if required
if (stripReleaseInfo) {
paragon.addAll(stripReleaseInfo(terms, true));
paragon.addAll(stripReleaseInfo(terms, false));
} else {
paragon.addAll(terms);
}
// similarity comparator with multi-value support // similarity comparator with multi-value support
SimilarityComparator comparator = new SimilarityComparator(metric, paragon.toArray()) { SimilarityComparator comparator = new SimilarityComparator(metric, terms.toArray()) {
@Override @Override
public float getMaxSimilarity(Object obj) { public float getMaxSimilarity(Object obj) {
@ -748,6 +738,20 @@ public class MediaDetection {
return result; return result;
} }
public static <T> List<T> sortBySimilarity(Collection<T> options, Collection<String> terms, SimilarityMetric metric, boolean stripReleaseInfo) throws IOException {
Collection<String> paragon = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
// clean clutter tokens if required
if (stripReleaseInfo) {
paragon.addAll(stripReleaseInfo(terms, true));
paragon.addAll(stripReleaseInfo(terms, false));
} else {
paragon.addAll(terms);
}
return sortBySimilarity(options, paragon, metric);
}
public static boolean isEpisodeNumberMatch(File f, Episode e) { public static boolean isEpisodeNumberMatch(File f, Episode e) {
float similarity = EpisodeMetrics.EpisodeIdentifier.getSimilarity(f, e); float similarity = EpisodeMetrics.EpisodeIdentifier.getSimilarity(f, e);
if (similarity >= 1) { if (similarity >= 1) {
@ -1266,29 +1270,42 @@ public class MediaDetection {
return WebServices.TheTVDB.getSeriesInfo(thetvdbid, locale); return WebServices.TheTVDB.getSeriesInfo(thetvdbid, locale);
} }
public static List<SearchResult> getProbableMatches(String query, Collection<SearchResult> options) { public static List<SearchResult> getProbableMatches(String query, Collection<? extends SearchResult> options, boolean strict) {
if (query == null) {
return new ArrayList<SearchResult>(options);
}
// auto-select most probable search result // auto-select most probable search result
List<SearchResult> probableMatches = new LinkedList<SearchResult>(); List<SearchResult> probableMatches = new ArrayList<SearchResult>();
// use name similarity metric // use name similarity metric
SimilarityMetric metric = new NameSimilarityMetric(); SimilarityMetric metric = new NameSimilarityMetric();
float threshold = 0.85f; float threshold = strict && options.size() > 1 ? 0.8f : 0.6f;
float sanity = strict && options.size() > 1 ? 0.5f : 0.2f;
// remove trailing braces, e.g. Doctor Who (2005) -> Doctor Who // remove trailing braces, e.g. Doctor Who (2005) -> doctor who
query = removeTrailingBrackets(query); query = removeTrailingBrackets(query).toLowerCase();
// find probable matches using name similarity >= 0.85 // find probable matches using name similarity > 0.8 (or > 0.6 in non-strict mode)
for (SearchResult option : options) { for (SearchResult option : options) {
float f = 0; float f = 0;
for (String n : option.getEffectiveNames()) { for (String n : option.getEffectiveNames()) {
f = Math.max(f, metric.getSimilarity(query, removeTrailingBrackets(n))); n = removeTrailingBrackets(n).toLowerCase();
f = Math.max(f, metric.getSimilarity(query, n));
// boost matching beginnings
if (f >= sanity && n.startsWith(query)) {
f = 1;
break;
}
} }
if (f >= threshold) {
if (f >= threshold && !probableMatches.contains(option)) {
probableMatches.add(option); probableMatches.add(option);
} }
} }
return probableMatches; return sortBySimilarity(probableMatches, singleton(query), new NameSimilarityMetric());
} }
public static class IndexEntry<T> implements Serializable { public static class IndexEntry<T> implements Serializable {

View File

@ -70,7 +70,7 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
} }
// auto-select most probable search result // auto-select most probable search result
List<SearchResult> probableMatches = getProbableMatches(query, searchResults); List<SearchResult> probableMatches = getProbableMatches(query, searchResults, true);
// auto-select first and only probable search result // auto-select first and only probable search result
if (probableMatches.size() == 1) { if (probableMatches.size() == 1) {