* ask for user interaction once per unique query at most
This commit is contained in:
parent
b10b6743f6
commit
25742ba566
|
@ -20,9 +20,11 @@ import java.util.LinkedHashSet;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.SortedSet;
|
import java.util.SortedSet;
|
||||||
|
import java.util.TreeMap;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
|
@ -35,6 +37,7 @@ import javax.swing.Action;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
|
|
||||||
import net.sourceforge.filebot.Analytics;
|
import net.sourceforge.filebot.Analytics;
|
||||||
|
import net.sourceforge.filebot.similarity.CommonSequenceMatcher;
|
||||||
import net.sourceforge.filebot.similarity.EpisodeMatcher;
|
import net.sourceforge.filebot.similarity.EpisodeMatcher;
|
||||||
import net.sourceforge.filebot.similarity.Match;
|
import net.sourceforge.filebot.similarity.Match;
|
||||||
import net.sourceforge.filebot.similarity.NameSimilarityMetric;
|
import net.sourceforge.filebot.similarity.NameSimilarityMetric;
|
||||||
|
@ -60,7 +63,7 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected SearchResult selectSearchResult(final String query, final List<SearchResult> searchResults, final Component parent) throws Exception {
|
protected SearchResult selectSearchResult(final String query, final List<SearchResult> searchResults, Map<String, SearchResult> selectionMemory, final Component parent) throws Exception {
|
||||||
if (searchResults.size() == 1) {
|
if (searchResults.size() == 1) {
|
||||||
return searchResults.get(0);
|
return searchResults.get(0);
|
||||||
}
|
}
|
||||||
|
@ -71,9 +74,10 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
|
||||||
// use name similarity metric
|
// use name similarity metric
|
||||||
SimilarityMetric metric = new NameSimilarityMetric();
|
SimilarityMetric metric = new NameSimilarityMetric();
|
||||||
|
|
||||||
// find probable matches using name similarity >= 0.9
|
// find probable matches using name similarity >= 0.85
|
||||||
for (SearchResult result : searchResults) {
|
for (SearchResult result : searchResults) {
|
||||||
if (metric.getSimilarity(normalizeName(query), normalizeName(result.getName())) >= 0.9) {
|
// remove trailing braces, e.g. Doctor Who (2005) -> Doctor Who
|
||||||
|
if (metric.getSimilarity(removeTrailingBrackets(query), removeTrailingBrackets(result.getName())) >= 0.85) {
|
||||||
probableMatches.add(result);
|
probableMatches.add(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -107,21 +111,26 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
|
||||||
|
|
||||||
// allow only one select dialog at a time
|
// allow only one select dialog at a time
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
|
synchronized (selectionMemory) {
|
||||||
|
SearchResult selection = selectionMemory.get(query);
|
||||||
|
if (selection != null) {
|
||||||
|
return selection;
|
||||||
|
}
|
||||||
|
|
||||||
SwingUtilities.invokeAndWait(showSelectDialog);
|
SwingUtilities.invokeAndWait(showSelectDialog);
|
||||||
}
|
|
||||||
|
|
||||||
// selected value or null
|
// cache selected value
|
||||||
return showSelectDialog.get();
|
selection = showSelectDialog.get();
|
||||||
|
if (selection != null) {
|
||||||
|
selectionMemory.put(query, selection);
|
||||||
|
}
|
||||||
|
return selection;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private String normalizeName(String value) {
|
protected Set<Episode> fetchEpisodeSet(Collection<String> seriesNames, final SortOrder sortOrder, final Locale locale, final Map<String, SearchResult> selectionMemory, final Component parent) throws Exception {
|
||||||
// remove trailing braces, e.g. Doctor Who (2005) -> doctor who
|
|
||||||
return removeTrailingBrackets(value).toLowerCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected Set<Episode> fetchEpisodeSet(Collection<String> seriesNames, final SortOrder sortOrder, final Locale locale, final Component parent) throws Exception {
|
|
||||||
List<Callable<List<Episode>>> tasks = new ArrayList<Callable<List<Episode>>>();
|
List<Callable<List<Episode>>> tasks = new ArrayList<Callable<List<Episode>>>();
|
||||||
|
|
||||||
// detect series names and create episode list fetch tasks
|
// detect series names and create episode list fetch tasks
|
||||||
|
@ -134,7 +143,7 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
|
||||||
|
|
||||||
// select search result
|
// select search result
|
||||||
if (results.size() > 0) {
|
if (results.size() > 0) {
|
||||||
SearchResult selectedSearchResult = selectSearchResult(query, results, parent);
|
SearchResult selectedSearchResult = selectSearchResult(query, results, selectionMemory, parent);
|
||||||
|
|
||||||
if (selectedSearchResult != null) {
|
if (selectedSearchResult != null) {
|
||||||
List<Episode> episodes = provider.getEpisodeList(selectedSearchResult, sortOrder, locale);
|
List<Episode> episodes = provider.getEpisodeList(selectedSearchResult, sortOrder, locale);
|
||||||
|
@ -177,6 +186,9 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
|
||||||
// assume that many shows will be matched, do it folder by folder
|
// assume that many shows will be matched, do it folder by folder
|
||||||
List<Callable<List<Match<File, ?>>>> taskPerFolder = new ArrayList<Callable<List<Match<File, ?>>>>();
|
List<Callable<List<Match<File, ?>>>> taskPerFolder = new ArrayList<Callable<List<Match<File, ?>>>>();
|
||||||
|
|
||||||
|
// remember user decisions and only bother user once
|
||||||
|
final Map<String, SearchResult> selectionMemory = new TreeMap<String, SearchResult>(CommonSequenceMatcher.getLenientCollator(Locale.ROOT));
|
||||||
|
|
||||||
// detect series names and create episode list fetch tasks
|
// detect series names and create episode list fetch tasks
|
||||||
for (Entry<Set<File>, Set<String>> sameSeriesGroup : mapSeriesNamesByFiles(mediaFiles, locale).entrySet()) {
|
for (Entry<Set<File>, Set<String>> sameSeriesGroup : mapSeriesNamesByFiles(mediaFiles, locale).entrySet()) {
|
||||||
final List<List<File>> batchSets = new ArrayList<List<File>>();
|
final List<List<File>> batchSets = new ArrayList<List<File>>();
|
||||||
|
@ -195,7 +207,7 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Match<File, ?>> call() throws Exception {
|
public List<Match<File, ?>> call() throws Exception {
|
||||||
return matchEpisodeSet(batchSet, queries, sortOrder, locale, autodetection, parent);
|
return matchEpisodeSet(batchSet, queries, sortOrder, locale, autodetection, selectionMemory, parent);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -246,7 +258,7 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public List<Match<File, ?>> matchEpisodeSet(final List<File> files, Collection<String> queries, SortOrder sortOrder, Locale locale, boolean autodetection, Component parent) throws Exception {
|
public List<Match<File, ?>> matchEpisodeSet(final List<File> files, Collection<String> queries, SortOrder sortOrder, Locale locale, boolean autodetection, Map<String, SearchResult> selectionMemory, Component parent) throws Exception {
|
||||||
Set<Episode> episodes = emptySet();
|
Set<Episode> episodes = emptySet();
|
||||||
|
|
||||||
// detect series name and fetch episode list
|
// detect series name and fetch episode list
|
||||||
|
@ -254,7 +266,7 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
|
||||||
if (queries != null && queries.size() > 0) {
|
if (queries != null && queries.size() > 0) {
|
||||||
// only allow one fetch session at a time so later requests can make use of cached results
|
// only allow one fetch session at a time so later requests can make use of cached results
|
||||||
synchronized (providerLock) {
|
synchronized (providerLock) {
|
||||||
episodes = fetchEpisodeSet(queries, sortOrder, locale, parent);
|
episodes = fetchEpisodeSet(queries, sortOrder, locale, selectionMemory, parent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -278,7 +290,7 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
|
||||||
if (input.size() > 0) {
|
if (input.size() > 0) {
|
||||||
// only allow one fetch session at a time so later requests can make use of cached results
|
// only allow one fetch session at a time so later requests can make use of cached results
|
||||||
synchronized (providerLock) {
|
synchronized (providerLock) {
|
||||||
episodes = fetchEpisodeSet(input, sortOrder, locale, parent);
|
episodes = fetchEpisodeSet(input, sortOrder, locale, selectionMemory, parent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue