mirror of
https://github.com/mitb-archive/filebot
synced 2025-01-10 21:38:04 -05:00
Support Query->SearchResult persistent memory in Episode mode
This commit is contained in:
parent
6f7169e255
commit
74569a405f
@ -3,7 +3,7 @@
|
|||||||
Persistent disk store location
|
Persistent disk store location
|
||||||
-->
|
-->
|
||||||
<diskStore path="ehcache.disk.store.dir" />
|
<diskStore path="ehcache.disk.store.dir" />
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Mandatory Default Cache configuration. These settings will be applied to caches
|
Mandatory Default Cache configuration. These settings will be applied to caches
|
||||||
created pragmatically using CacheManager.add(String cacheName)
|
created pragmatically using CacheManager.add(String cacheName)
|
||||||
@ -17,7 +17,7 @@
|
|||||||
diskPersistent="false"
|
diskPersistent="false"
|
||||||
memoryStoreEvictionPolicy="LRU"
|
memoryStoreEvictionPolicy="LRU"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Short-lived (24 hours) persistent disk cache for web responses
|
Short-lived (24 hours) persistent disk cache for web responses
|
||||||
-->
|
-->
|
||||||
@ -31,7 +31,7 @@
|
|||||||
diskPersistent="true"
|
diskPersistent="true"
|
||||||
memoryStoreEvictionPolicy="LRU"
|
memoryStoreEvictionPolicy="LRU"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Long-lived (2 weeks) persistent disk cache for web responses
|
Long-lived (2 weeks) persistent disk cache for web responses
|
||||||
-->
|
-->
|
||||||
@ -45,7 +45,7 @@
|
|||||||
diskPersistent="true"
|
diskPersistent="true"
|
||||||
memoryStoreEvictionPolicy="LRU"
|
memoryStoreEvictionPolicy="LRU"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Long-lived (2 months) persistent disk cache for web responses (that can be updated via If-Modified or If-None-Match)
|
Long-lived (2 months) persistent disk cache for web responses (that can be updated via If-Modified or If-None-Match)
|
||||||
-->
|
-->
|
||||||
@ -59,7 +59,7 @@
|
|||||||
diskPersistent="true"
|
diskPersistent="true"
|
||||||
memoryStoreEvictionPolicy="LRU"
|
memoryStoreEvictionPolicy="LRU"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Very long-lived cache (4 months) anime/series lists, movie index, etc
|
Very long-lived cache (4 months) anime/series lists, movie index, etc
|
||||||
-->
|
-->
|
||||||
@ -73,11 +73,25 @@
|
|||||||
diskPersistent="true"
|
diskPersistent="true"
|
||||||
memoryStoreEvictionPolicy="LRU"
|
memoryStoreEvictionPolicy="LRU"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Simple memory cache. Time to live is 4 months.
|
||||||
|
-->
|
||||||
|
<cache name="persistent-memory"
|
||||||
|
maxElementsInMemory="100"
|
||||||
|
maxElementsOnDisk="50000"
|
||||||
|
eternal="false"
|
||||||
|
timeToIdleSeconds="10512000"
|
||||||
|
timeToLiveSeconds="10512000"
|
||||||
|
overflowToDisk="true"
|
||||||
|
diskPersistent="true"
|
||||||
|
memoryStoreEvictionPolicy="LRU"
|
||||||
|
/>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Simple memory cache. Time to live is 2 hours.
|
Simple memory cache. Time to live is 2 hours.
|
||||||
-->
|
-->
|
||||||
<cache name="ephemeral"
|
<cache name="ephemeral-memory"
|
||||||
maxElementsInMemory="50000"
|
maxElementsInMemory="50000"
|
||||||
eternal="false"
|
eternal="false"
|
||||||
timeToIdleSeconds="7200"
|
timeToIdleSeconds="7200"
|
||||||
@ -86,5 +100,5 @@
|
|||||||
diskPersistent="false"
|
diskPersistent="false"
|
||||||
memoryStoreEvictionPolicy="LRU"
|
memoryStoreEvictionPolicy="LRU"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</ehcache>
|
</ehcache>
|
||||||
|
@ -10,7 +10,8 @@ import net.sf.ehcache.Element;
|
|||||||
|
|
||||||
public class Cache {
|
public class Cache {
|
||||||
|
|
||||||
public static final String EPHEMERAL = "ephemeral";
|
public static final String EPHEMERAL = "ephemeral-memory";
|
||||||
|
public static final String PERSISTENT = "persistent-memory";
|
||||||
|
|
||||||
public static Cache getCache(String name) {
|
public static Cache getCache(String name) {
|
||||||
return new Cache(CacheManager.getInstance().getCache(name));
|
return new Cache(CacheManager.getInstance().getCache(name));
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package net.filebot.ui;
|
package net.filebot.ui;
|
||||||
|
|
||||||
|
import static java.awt.Cursor.*;
|
||||||
import static net.filebot.util.ui.SwingUI.*;
|
import static net.filebot.util.ui.SwingUI.*;
|
||||||
|
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
@ -13,6 +14,7 @@ import java.util.Collection;
|
|||||||
import javax.swing.AbstractAction;
|
import javax.swing.AbstractAction;
|
||||||
import javax.swing.Action;
|
import javax.swing.Action;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
|
import javax.swing.JCheckBox;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JDialog;
|
import javax.swing.JDialog;
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
@ -30,12 +32,17 @@ import net.miginfocom.swing.MigLayout;
|
|||||||
public class SelectDialog<T> extends JDialog {
|
public class SelectDialog<T> extends JDialog {
|
||||||
|
|
||||||
private JLabel headerLabel = new JLabel();
|
private JLabel headerLabel = new JLabel();
|
||||||
|
private JCheckBox autoRepeatCheckBox = new JCheckBox();
|
||||||
|
|
||||||
private JList list;
|
private JList list;
|
||||||
|
|
||||||
private Action selectedAction = null;
|
private Action selectedAction = null;
|
||||||
|
|
||||||
public SelectDialog(Component parent, Collection<? extends T> options) {
|
public SelectDialog(Component parent, Collection<? extends T> options) {
|
||||||
|
this(parent, options, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SelectDialog(Component parent, Collection<? extends T> options, boolean autoRepeatEnabled, boolean autoRepeatSelected) {
|
||||||
super(getWindow(parent), "Select", ModalityType.DOCUMENT_MODAL);
|
super(getWindow(parent), "Select", ModalityType.DOCUMENT_MODAL);
|
||||||
|
|
||||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||||
@ -70,6 +77,16 @@ public class SelectDialog<T> extends JDialog {
|
|||||||
c.add(new JButton(selectAction), "align center, id select");
|
c.add(new JButton(selectAction), "align center, id select");
|
||||||
c.add(new JButton(cancelAction), "gap unrel, id cancel");
|
c.add(new JButton(cancelAction), "gap unrel, id cancel");
|
||||||
|
|
||||||
|
// add repeat button
|
||||||
|
if (autoRepeatEnabled) {
|
||||||
|
autoRepeatCheckBox.setSelected(autoRepeatSelected);
|
||||||
|
autoRepeatCheckBox.setToolTipText("Remember");
|
||||||
|
autoRepeatCheckBox.setCursor(getPredefinedCursor(HAND_CURSOR));
|
||||||
|
autoRepeatCheckBox.setIcon(ResourceManager.getIcon("button.repeat"));
|
||||||
|
autoRepeatCheckBox.setSelectedIcon(ResourceManager.getIcon("button.repeat.selected"));
|
||||||
|
c.add(autoRepeatCheckBox, "pos 1al select.y n select.y2");
|
||||||
|
}
|
||||||
|
|
||||||
// set default size and location
|
// set default size and location
|
||||||
setMinimumSize(new Dimension(220, 240));
|
setMinimumSize(new Dimension(220, 240));
|
||||||
setSize(new Dimension(240, 260));
|
setSize(new Dimension(240, 260));
|
||||||
@ -86,6 +103,10 @@ public class SelectDialog<T> extends JDialog {
|
|||||||
return headerLabel;
|
return headerLabel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public JCheckBox getAutoRepeatCheckBox() {
|
||||||
|
return autoRepeatCheckBox;
|
||||||
|
}
|
||||||
|
|
||||||
public Action getSelectedAction() {
|
public Action getSelectedAction() {
|
||||||
return selectedAction;
|
return selectedAction;
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,7 @@ import java.util.concurrent.RunnableFuture;
|
|||||||
import javax.swing.Action;
|
import javax.swing.Action;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
|
|
||||||
|
import net.filebot.Cache;
|
||||||
import net.filebot.Settings;
|
import net.filebot.Settings;
|
||||||
import net.filebot.similarity.CommonSequenceMatcher;
|
import net.filebot.similarity.CommonSequenceMatcher;
|
||||||
import net.filebot.similarity.EpisodeMatcher;
|
import net.filebot.similarity.EpisodeMatcher;
|
||||||
@ -58,13 +59,16 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
|
|||||||
// 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
|
||||||
private final Object providerLock = new Object();
|
private final Object providerLock = new Object();
|
||||||
|
|
||||||
|
// remember user selections
|
||||||
|
private final Cache persistentSelectionMemory = Cache.getCache(Cache.PERSISTENT);
|
||||||
|
|
||||||
public EpisodeListMatcher(EpisodeListProvider provider, boolean useSeriesIndex, boolean useAnimeIndex) {
|
public EpisodeListMatcher(EpisodeListProvider provider, boolean useSeriesIndex, boolean useAnimeIndex) {
|
||||||
this.provider = provider;
|
this.provider = provider;
|
||||||
this.useSeriesIndex = useSeriesIndex;
|
this.useSeriesIndex = useSeriesIndex;
|
||||||
this.useAnimeIndex = useAnimeIndex;
|
this.useAnimeIndex = useAnimeIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected SearchResult selectSearchResult(final String query, final List<SearchResult> searchResults, Map<String, SearchResult> selectionMemory, final Component parent) throws Exception {
|
protected SearchResult selectSearchResult(final String query, final List<SearchResult> searchResults, Map<String, SearchResult> selectionMemory, boolean autodetection, final Component parent) throws Exception {
|
||||||
if (searchResults.size() == 1) {
|
if (searchResults.size() == 1) {
|
||||||
return searchResults.get(0);
|
return searchResults.get(0);
|
||||||
}
|
}
|
||||||
@ -83,7 +87,7 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
|
|||||||
@Override
|
@Override
|
||||||
public SearchResult call() throws Exception {
|
public SearchResult call() throws Exception {
|
||||||
// multiple results have been found, user must select one
|
// multiple results have been found, user must select one
|
||||||
SelectDialog<SearchResult> selectDialog = new SelectDialog<SearchResult>(parent, searchResults);
|
SelectDialog<SearchResult> selectDialog = new SelectDialog<SearchResult>(parent, searchResults, true, false);
|
||||||
|
|
||||||
selectDialog.getHeaderLabel().setText(String.format("Shows matching '%s':", query));
|
selectDialog.getHeaderLabel().setText(String.format("Shows matching '%s':", query));
|
||||||
selectDialog.getCancelAction().putValue(Action.NAME, "Ignore");
|
selectDialog.getCancelAction().putValue(Action.NAME, "Ignore");
|
||||||
@ -107,6 +111,11 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
|
|||||||
throw new CancellationException("Cancelled by user");
|
throw new CancellationException("Cancelled by user");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// remember if we should auto-repeat the chosen action in the future
|
||||||
|
if (selectDialog.getAutoRepeatCheckBox().isSelected() && selectDialog.getSelectedValue() != null) {
|
||||||
|
persistentSelectionMemory.put(query, selectDialog.getSelectedValue());
|
||||||
|
}
|
||||||
|
|
||||||
// selected value or null if the dialog was canceled by the user
|
// selected value or null if the dialog was canceled by the user
|
||||||
return selectDialog.getSelectedValue();
|
return selectDialog.getSelectedValue();
|
||||||
}
|
}
|
||||||
@ -119,16 +128,26 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
|
|||||||
return selectionMemory.get(query);
|
return selectionMemory.get(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
SwingUtilities.invokeAndWait(showSelectDialog);
|
// check persistent memory
|
||||||
|
if (autodetection) {
|
||||||
|
SearchResult persistentSelection = persistentSelectionMemory.get(query, SearchResult.class);
|
||||||
|
if (persistentSelection != null) {
|
||||||
|
return persistentSelection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// cache selected value
|
// ask user
|
||||||
selectionMemory.put(query, showSelectDialog.get());
|
SwingUtilities.invokeAndWait(showSelectDialog);
|
||||||
return showSelectDialog.get();
|
SearchResult userSelection = showSelectDialog.get();
|
||||||
|
|
||||||
|
// remember selected value
|
||||||
|
selectionMemory.put(query, userSelection);
|
||||||
|
return userSelection;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Set<Episode> fetchEpisodeSet(Collection<String> seriesNames, final SortOrder sortOrder, final Locale locale, final Map<String, SearchResult> selectionMemory, final Component parent) throws Exception {
|
protected Set<Episode> fetchEpisodeSet(Collection<String> seriesNames, final SortOrder sortOrder, final Locale locale, final Map<String, SearchResult> selectionMemory, final boolean autodetection, 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
|
||||||
@ -141,7 +160,7 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
|
|||||||
|
|
||||||
// select search result
|
// select search result
|
||||||
if (results.size() > 0) {
|
if (results.size() > 0) {
|
||||||
SearchResult selectedSearchResult = selectSearchResult(query, results, selectionMemory, parent);
|
SearchResult selectedSearchResult = selectSearchResult(query, results, selectionMemory, autodetection, parent);
|
||||||
|
|
||||||
if (selectedSearchResult != null) {
|
if (selectedSearchResult != null) {
|
||||||
return provider.getEpisodeList(selectedSearchResult, sortOrder, locale);
|
return provider.getEpisodeList(selectedSearchResult, sortOrder, locale);
|
||||||
@ -278,7 +297,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, selectionMemory, parent);
|
episodes = fetchEpisodeSet(queries, sortOrder, locale, selectionMemory, autodetection, parent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -301,7 +320,7 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
|
|||||||
if (input != null && input.size() > 0) {
|
if (input != null && 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, new HashMap<String, SearchResult>(), parent);
|
episodes = fetchEpisodeSet(input, sortOrder, locale, new HashMap<String, SearchResult>(), false, parent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -374,7 +393,7 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
|
|||||||
List<Match<File, ?>> matches = new ArrayList<Match<File, ?>>();
|
List<Match<File, ?>> matches = new ArrayList<Match<File, ?>>();
|
||||||
if (input.size() > 0) {
|
if (input.size() > 0) {
|
||||||
synchronized (providerLock) {
|
synchronized (providerLock) {
|
||||||
Set<Episode> episodes = fetchEpisodeSet(input, sortOrder, locale, new HashMap<String, SearchResult>(), parent);
|
Set<Episode> episodes = fetchEpisodeSet(input, sortOrder, locale, new HashMap<String, SearchResult>(), false, parent);
|
||||||
for (Episode it : episodes) {
|
for (Episode it : episodes) {
|
||||||
matches.add(new Match<File, Episode>(null, it));
|
matches.add(new Match<File, Episode>(null, it));
|
||||||
}
|
}
|
||||||
|
@ -404,21 +404,12 @@ class MovieHashMatcher implements AutoCompleteMatcher {
|
|||||||
@Override
|
@Override
|
||||||
public Movie call() throws Exception {
|
public Movie call() throws Exception {
|
||||||
// multiple results have been found, user must select one
|
// multiple results have been found, user must select one
|
||||||
SelectDialog<Movie> selectDialog = new SelectDialog<Movie>(parent, options);
|
SelectDialog<Movie> selectDialog = new SelectDialog<Movie>(parent, options, true, false);
|
||||||
|
|
||||||
selectDialog.setTitle(folderQuery.isEmpty() ? fileQuery : String.format("%s / %s", folderQuery, fileQuery));
|
selectDialog.setTitle(folderQuery.isEmpty() ? fileQuery : String.format("%s / %s", folderQuery, fileQuery));
|
||||||
selectDialog.getHeaderLabel().setText(String.format("Movies matching '%s':", fileQuery.length() >= 2 || folderQuery.length() <= 2 ? fileQuery : folderQuery));
|
selectDialog.getHeaderLabel().setText(String.format("Movies matching '%s':", fileQuery.length() >= 2 || folderQuery.length() <= 2 ? fileQuery : folderQuery));
|
||||||
selectDialog.getCancelAction().putValue(Action.NAME, "Ignore");
|
selectDialog.getCancelAction().putValue(Action.NAME, "Ignore");
|
||||||
|
|
||||||
// add repeat button
|
|
||||||
JCheckBox checkBox = new JCheckBox();
|
|
||||||
checkBox.setToolTipText("Select / Ignore for all");
|
|
||||||
checkBox.setCursor(getPredefinedCursor(HAND_CURSOR));
|
|
||||||
checkBox.setIcon(ResourceManager.getIcon("button.repeat"));
|
|
||||||
checkBox.setSelectedIcon(ResourceManager.getIcon("button.repeat.selected"));
|
|
||||||
JComponent c = (JComponent) selectDialog.getContentPane();
|
|
||||||
c.add(checkBox, "pos 1al select.y n select.y2");
|
|
||||||
|
|
||||||
// restore original dialog size
|
// restore original dialog size
|
||||||
Settings prefs = Settings.forPackage(MovieHashMatcher.class);
|
Settings prefs = Settings.forPackage(MovieHashMatcher.class);
|
||||||
int w = Integer.parseInt(prefs.get("dialog.select.w", "280"));
|
int w = Integer.parseInt(prefs.get("dialog.select.w", "280"));
|
||||||
@ -435,7 +426,7 @@ class MovieHashMatcher implements AutoCompleteMatcher {
|
|||||||
prefs.put("dialog.select.h", Integer.toString(selectDialog.getHeight()));
|
prefs.put("dialog.select.h", Integer.toString(selectDialog.getHeight()));
|
||||||
|
|
||||||
// remember if we should auto-repeat the chosen action in the future
|
// remember if we should auto-repeat the chosen action in the future
|
||||||
if (checkBox.isSelected() || selectDialog.getSelectedAction() == null) {
|
if (selectDialog.getAutoRepeatCheckBox().isSelected() || selectDialog.getSelectedAction() == null) {
|
||||||
memory.put("repeat", selectDialog.getSelectedValue() != null ? "select" : "ignore");
|
memory.put("repeat", selectDialog.getSelectedValue() != null ? "select" : "ignore");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user