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:
parent
8de14a1b71
commit
40a98b08ae
@ -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()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user