* remember recent formats in EpisodeFormatDialog
* display script exceptions if formatted name is empty * better handling of empty search results in some page scrapes * some test cases * refactoring
This commit is contained in:
parent
c4ce1aebe7
commit
78b77034b1
|
@ -2,8 +2,6 @@
|
||||||
package net.sourceforge.filebot;
|
package net.sourceforge.filebot;
|
||||||
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.prefs.BackingStoreException;
|
import java.util.prefs.BackingStoreException;
|
||||||
import java.util.prefs.Preferences;
|
import java.util.prefs.Preferences;
|
||||||
|
|
||||||
|
@ -26,6 +24,7 @@ public final class Settings {
|
||||||
return "1.9";
|
return "1.9";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
private static final Settings userRoot = new Settings(Preferences.userNodeForPackage(Settings.class));
|
private static final Settings userRoot = new Settings(Preferences.userNodeForPackage(Settings.class));
|
||||||
|
|
||||||
|
|
||||||
|
@ -33,6 +32,7 @@ public final class Settings {
|
||||||
return userRoot;
|
return userRoot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private final Preferences prefs;
|
private final Preferences prefs;
|
||||||
|
|
||||||
|
|
||||||
|
@ -83,22 +83,22 @@ public final class Settings {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Map<String, String> asMap() {
|
public PreferencesMap<String> asMap() {
|
||||||
return PreferencesMap.map(prefs);
|
return PreferencesMap.map(prefs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public <T> Map<String, T> asMap(Adapter<T> adapter) {
|
public <T> PreferencesMap<T> asMap(Adapter<T> adapter) {
|
||||||
return PreferencesMap.map(prefs, adapter);
|
return PreferencesMap.map(prefs, adapter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public List<String> asList() {
|
public PreferencesList<String> asList() {
|
||||||
return PreferencesList.map(prefs);
|
return PreferencesList.map(prefs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public <T> List<T> asList(Adapter<T> adapter) {
|
public <T> PreferencesList<T> asList(Adapter<T> adapter) {
|
||||||
return PreferencesList.map(prefs, adapter);
|
return PreferencesList.map(prefs, adapter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,13 @@ importPackage(java.lang);
|
||||||
importPackage(java.util);
|
importPackage(java.util);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience methods for String.toLowerCase() and String.toUpperCase().
|
||||||
|
*/
|
||||||
|
String.prototype.lower = String.prototype.toLowerCase;
|
||||||
|
String.prototype.upper = String.prototype.toUpperCase;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pad strings or numbers with given characters ('0' by default).
|
* Pad strings or numbers with given characters ('0' by default).
|
||||||
*
|
*
|
||||||
|
|
|
@ -117,6 +117,9 @@ public class ExpressionFormat extends Format {
|
||||||
ScriptContext context = new SimpleScriptContext();
|
ScriptContext context = new SimpleScriptContext();
|
||||||
context.setBindings(priviledgedBindings, ScriptContext.GLOBAL_SCOPE);
|
context.setBindings(priviledgedBindings, ScriptContext.GLOBAL_SCOPE);
|
||||||
|
|
||||||
|
// reset exception state
|
||||||
|
lastException = null;
|
||||||
|
|
||||||
for (Object snipped : compilation) {
|
for (Object snipped : compilation) {
|
||||||
if (snipped instanceof CompiledScript) {
|
if (snipped instanceof CompiledScript) {
|
||||||
try {
|
try {
|
||||||
|
@ -146,7 +149,7 @@ public class ExpressionFormat extends Format {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public ScriptException scriptException() {
|
public ScriptException caughtScriptException() {
|
||||||
return lastException;
|
return lastException;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,10 +13,18 @@ import net.sourceforge.filebot.web.Episode;
|
||||||
import net.sourceforge.filebot.web.EpisodeFormat;
|
import net.sourceforge.filebot.web.EpisodeFormat;
|
||||||
|
|
||||||
|
|
||||||
class EpisodeExpressionFormatter extends ExpressionFormat implements MatchFormatter {
|
class EpisodeExpressionFormatter implements MatchFormatter {
|
||||||
|
|
||||||
public EpisodeExpressionFormatter(String expression) throws ScriptException {
|
private final ExpressionFormat format;
|
||||||
super(expression);
|
|
||||||
|
|
||||||
|
public EpisodeExpressionFormatter(ExpressionFormat format) {
|
||||||
|
this.format = format;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public ExpressionFormat getFormat() {
|
||||||
|
return format;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -34,11 +42,17 @@ class EpisodeExpressionFormatter extends ExpressionFormat implements MatchFormat
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String format(Match<?, ?> match) {
|
public synchronized String format(Match<?, ?> match) throws ScriptException {
|
||||||
Episode episode = (Episode) match.getValue();
|
Episode episode = (Episode) match.getValue();
|
||||||
File mediaFile = (File) match.getCandidate();
|
File mediaFile = (File) match.getCandidate();
|
||||||
|
|
||||||
return format(new EpisodeFormatBindingBean(episode, mediaFile)).trim();
|
String result = format.format(new EpisodeFormatBindingBean(episode, mediaFile)).trim();
|
||||||
|
|
||||||
|
// if result is empty, check for script exceptions
|
||||||
|
if (result.isEmpty() && format.caughtScriptException() != null)
|
||||||
|
throw format.caughtScriptException();
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
|
|
||||||
package net.sourceforge.filebot.ui;
|
package net.sourceforge.filebot.ui.panel.rename;
|
||||||
|
|
||||||
|
|
||||||
import static java.awt.Font.*;
|
import static java.awt.Font.*;
|
||||||
|
import static javax.swing.BorderFactory.*;
|
||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Font;
|
import java.awt.Font;
|
||||||
|
@ -16,8 +17,10 @@ import java.io.File;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.ArrayBlockingQueue;
|
import java.util.concurrent.ArrayBlockingQueue;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
@ -34,16 +37,16 @@ import javax.swing.JComponent;
|
||||||
import javax.swing.JDialog;
|
import javax.swing.JDialog;
|
||||||
import javax.swing.JFileChooser;
|
import javax.swing.JFileChooser;
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JMenuItem;
|
||||||
import javax.swing.JOptionPane;
|
import javax.swing.JOptionPane;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import javax.swing.JPopupMenu;
|
import javax.swing.JPopupMenu;
|
||||||
import javax.swing.JTextField;
|
import javax.swing.JTextField;
|
||||||
|
import javax.swing.KeyStroke;
|
||||||
import javax.swing.SwingWorker;
|
import javax.swing.SwingWorker;
|
||||||
import javax.swing.Timer;
|
import javax.swing.Timer;
|
||||||
import javax.swing.border.LineBorder;
|
|
||||||
import javax.swing.event.DocumentEvent;
|
import javax.swing.event.DocumentEvent;
|
||||||
import javax.swing.filechooser.FileNameExtensionFilter;
|
import javax.swing.filechooser.FileNameExtensionFilter;
|
||||||
import javax.swing.undo.UndoManager;
|
|
||||||
|
|
||||||
import net.miginfocom.swing.MigLayout;
|
import net.miginfocom.swing.MigLayout;
|
||||||
import net.sourceforge.filebot.ResourceManager;
|
import net.sourceforge.filebot.ResourceManager;
|
||||||
|
@ -54,6 +57,7 @@ import net.sourceforge.filebot.web.Episode;
|
||||||
import net.sourceforge.filebot.web.EpisodeFormat;
|
import net.sourceforge.filebot.web.EpisodeFormat;
|
||||||
import net.sourceforge.tuned.DefaultThreadFactory;
|
import net.sourceforge.tuned.DefaultThreadFactory;
|
||||||
import net.sourceforge.tuned.ExceptionUtilities;
|
import net.sourceforge.tuned.ExceptionUtilities;
|
||||||
|
import net.sourceforge.tuned.PreferencesList;
|
||||||
import net.sourceforge.tuned.ui.GradientStyle;
|
import net.sourceforge.tuned.ui.GradientStyle;
|
||||||
import net.sourceforge.tuned.ui.LazyDocumentListener;
|
import net.sourceforge.tuned.ui.LazyDocumentListener;
|
||||||
import net.sourceforge.tuned.ui.LinkButton;
|
import net.sourceforge.tuned.ui.LinkButton;
|
||||||
|
@ -67,6 +71,8 @@ public class EpisodeFormatDialog extends JDialog {
|
||||||
|
|
||||||
private Option selectedOption = Option.CANCEL;
|
private Option selectedOption = Option.CANCEL;
|
||||||
|
|
||||||
|
private ExpressionFormat selectedFormat = null;
|
||||||
|
|
||||||
private JLabel preview = new JLabel();
|
private JLabel preview = new JLabel();
|
||||||
|
|
||||||
private JLabel status = new JLabel();
|
private JLabel status = new JLabel();
|
||||||
|
@ -79,6 +85,8 @@ public class EpisodeFormatDialog extends JDialog {
|
||||||
|
|
||||||
private JTextField editor = new JTextField();
|
private JTextField editor = new JTextField();
|
||||||
|
|
||||||
|
private PreferencesList<String> persistentFormatHistory = Settings.userRoot().node("rename/format.recent").asList();
|
||||||
|
|
||||||
private Color defaultColor = preview.getForeground();
|
private Color defaultColor = preview.getForeground();
|
||||||
private Color errorColor = Color.red;
|
private Color errorColor = Color.red;
|
||||||
|
|
||||||
|
@ -93,7 +101,6 @@ public class EpisodeFormatDialog extends JDialog {
|
||||||
public EpisodeFormatDialog(Window owner) {
|
public EpisodeFormatDialog(Window owner) {
|
||||||
super(owner, "Episode Format", ModalityType.DOCUMENT_MODAL);
|
super(owner, "Episode Format", ModalityType.DOCUMENT_MODAL);
|
||||||
|
|
||||||
editor.setText(Settings.userRoot().get("dialog.format"));
|
|
||||||
editor.setFont(new Font(MONOSPACED, PLAIN, 14));
|
editor.setFont(new Font(MONOSPACED, PLAIN, 14));
|
||||||
|
|
||||||
// bold title label in header
|
// bold title label in header
|
||||||
|
@ -132,10 +139,6 @@ public class EpisodeFormatDialog extends JDialog {
|
||||||
|
|
||||||
header.setComponentPopupMenu(createPreviewSamplePopup());
|
header.setComponentPopupMenu(createPreviewSamplePopup());
|
||||||
|
|
||||||
// setup undo support
|
|
||||||
final UndoManager undo = new UndoManager();
|
|
||||||
editor.getDocument().addUndoableEditListener(undo);
|
|
||||||
|
|
||||||
// enable undo/redo
|
// enable undo/redo
|
||||||
TunedUtilities.installUndoSupport(editor);
|
TunedUtilities.installUndoSupport(editor);
|
||||||
|
|
||||||
|
@ -174,6 +177,12 @@ public class EpisodeFormatDialog extends JDialog {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// install editor suggestions popup
|
||||||
|
TunedUtilities.installAction(editor, KeyStroke.getKeyStroke("DOWN"), displayRecentFormatHistory);
|
||||||
|
|
||||||
|
// restore editor state
|
||||||
|
editor.setText(persistentFormatHistory.isEmpty() ? "" : persistentFormatHistory.get(0));
|
||||||
|
|
||||||
// update preview to current format
|
// update preview to current format
|
||||||
firePreviewSampleChanged();
|
firePreviewSampleChanged();
|
||||||
|
|
||||||
|
@ -244,7 +253,7 @@ public class EpisodeFormatDialog extends JDialog {
|
||||||
private JPanel createSyntaxPanel() {
|
private JPanel createSyntaxPanel() {
|
||||||
JPanel panel = new JPanel(new MigLayout("fill, nogrid"));
|
JPanel panel = new JPanel(new MigLayout("fill, nogrid"));
|
||||||
|
|
||||||
panel.setBorder(new LineBorder(new Color(0xACA899)));
|
panel.setBorder(createLineBorder(new Color(0xACA899)));
|
||||||
panel.setBackground(new Color(0xFFFFE1));
|
panel.setBackground(new Color(0xFFFFE1));
|
||||||
panel.setOpaque(true);
|
panel.setOpaque(true);
|
||||||
|
|
||||||
|
@ -257,7 +266,7 @@ public class EpisodeFormatDialog extends JDialog {
|
||||||
private JComponent createExamplesPanel() {
|
private JComponent createExamplesPanel() {
|
||||||
JPanel panel = new JPanel(new MigLayout("fill, wrap 3"));
|
JPanel panel = new JPanel(new MigLayout("fill, wrap 3"));
|
||||||
|
|
||||||
panel.setBorder(new LineBorder(new Color(0xACA899)));
|
panel.setBorder(createLineBorder(new Color(0xACA899)));
|
||||||
panel.setBackground(new Color(0xFFFFE1));
|
panel.setBackground(new Color(0xFFFFE1));
|
||||||
|
|
||||||
ResourceBundle bundle = ResourceBundle.getBundle(getClass().getName());
|
ResourceBundle bundle = ResourceBundle.getBundle(getClass().getName());
|
||||||
|
@ -357,10 +366,10 @@ public class EpisodeFormatDialog extends JDialog {
|
||||||
threadGroup.stop();
|
threadGroup.stop();
|
||||||
|
|
||||||
// log access of potentially unsafe method
|
// log access of potentially unsafe method
|
||||||
Logger.getLogger("global").warning("Thread was forcibly terminated");
|
Logger.getLogger(getClass().getName()).warning("Thread was forcibly terminated");
|
||||||
}
|
}
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
Logger.getLogger("global").log(Level.WARNING, "Thread was not terminated", e);
|
Logger.getLogger(getClass().getName()).log(Level.WARNING, "Thread was not terminated", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return remaining;
|
return remaining;
|
||||||
|
@ -377,7 +386,7 @@ public class EpisodeFormatDialog extends JDialog {
|
||||||
private void checkFormatInBackground() {
|
private void checkFormatInBackground() {
|
||||||
try {
|
try {
|
||||||
// check syntax in foreground
|
// check syntax in foreground
|
||||||
final ExpressionFormat format = new ExpressionFormat(getExpression());
|
final ExpressionFormat format = new ExpressionFormat(editor.getText().trim());
|
||||||
|
|
||||||
// format in background
|
// format in background
|
||||||
final Timer progressIndicatorTimer = TunedUtilities.invokeLater(400, new Runnable() {
|
final Timer progressIndicatorTimer = TunedUtilities.invokeLater(400, new Runnable() {
|
||||||
|
@ -402,8 +411,8 @@ public class EpisodeFormatDialog extends JDialog {
|
||||||
preview.setText(get());
|
preview.setText(get());
|
||||||
|
|
||||||
// check internal script exception
|
// check internal script exception
|
||||||
if (format.scriptException() != null) {
|
if (format.caughtScriptException() != null) {
|
||||||
throw format.scriptException();
|
throw format.caughtScriptException();
|
||||||
}
|
}
|
||||||
|
|
||||||
// check empty output
|
// check empty output
|
||||||
|
@ -438,13 +447,13 @@ public class EpisodeFormatDialog extends JDialog {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public String getExpression() {
|
public Option getSelectedOption() {
|
||||||
return editor.getText().trim();
|
return selectedOption;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Option getSelectedOption() {
|
public ExpressionFormat getSelectedFormat() {
|
||||||
return selectedOption;
|
return selectedFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -459,6 +468,29 @@ public class EpisodeFormatDialog extends JDialog {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected final Action displayRecentFormatHistory = new AbstractAction("Recent") {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent evt) {
|
||||||
|
JPopupMenu popup = new JPopupMenu();
|
||||||
|
|
||||||
|
for (final String expression : persistentFormatHistory) {
|
||||||
|
JMenuItem item = popup.add(new AbstractAction(expression) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent evt) {
|
||||||
|
editor.setText(expression);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
item.setFont(new Font(MONOSPACED, PLAIN, 11));
|
||||||
|
}
|
||||||
|
|
||||||
|
// display popup below format editor
|
||||||
|
popup.show(editor, 0, editor.getHeight() + 3);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
protected final Action cancelAction = new AbstractAction("Cancel", ResourceManager.getIcon("dialog.cancel")) {
|
protected final Action cancelAction = new AbstractAction("Cancel", ResourceManager.getIcon("dialog.cancel")) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -480,17 +512,25 @@ public class EpisodeFormatDialog extends JDialog {
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent evt) {
|
public void actionPerformed(ActionEvent evt) {
|
||||||
try {
|
try {
|
||||||
if (progressIndicator.isVisible())
|
|
||||||
throw new IllegalStateException("Format has not been verified yet.");
|
|
||||||
|
|
||||||
// check syntax
|
// check syntax
|
||||||
ExpressionFormat format = new ExpressionFormat(getExpression());
|
selectedFormat = new ExpressionFormat(editor.getText().trim());
|
||||||
|
|
||||||
// remember format
|
// create new recent history and ignore duplicates
|
||||||
Settings.userRoot().put("dialog.format", format.getExpression());
|
Set<String> recent = new LinkedHashSet<String>();
|
||||||
|
|
||||||
|
// add new format first
|
||||||
|
recent.add(selectedFormat.getExpression());
|
||||||
|
|
||||||
|
// add next 4 most recent formats
|
||||||
|
for (int i = 0, limit = Math.min(4, persistentFormatHistory.size()); i < limit; i++) {
|
||||||
|
recent.add(persistentFormatHistory.get(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
// update persistent history
|
||||||
|
persistentFormatHistory.set(recent);
|
||||||
|
|
||||||
finish(Option.APPROVE);
|
finish(Option.APPROVE);
|
||||||
} catch (Exception e) {
|
} catch (ScriptException e) {
|
||||||
Logger.getLogger("ui").log(Level.WARNING, ExceptionUtilities.getRootCauseMessage(e));
|
Logger.getLogger("ui").log(Level.WARNING, ExceptionUtilities.getRootCauseMessage(e));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -10,4 +10,4 @@ example[1]: {n} - {'S'+s.pad(2)}E{e.pad(2)} - {t}
|
||||||
example[2]: {n} - {s+'x'}{e.pad(2)}
|
example[2]: {n} - {s+'x'}{e.pad(2)}
|
||||||
|
|
||||||
# uglyfy name
|
# uglyfy name
|
||||||
example[3]: {n.space('.').toLowerCase()}.{s}{e.pad(2)}
|
example[3]: {n.space('.').lower()}.{s}{e.pad(2)}
|
|
@ -20,6 +20,7 @@ final class HistorySpooler {
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private final File file = new File("history.xml");
|
private final File file = new File("history.xml");
|
||||||
|
|
||||||
private final History sessionHistory = new History();
|
private final History sessionHistory = new History();
|
||||||
|
@ -33,7 +34,7 @@ final class HistorySpooler {
|
||||||
try {
|
try {
|
||||||
history.addAll(importHistory(file).sequences());
|
history.addAll(importHistory(file).sequences());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Logger.getLogger("global").log(Level.SEVERE, "Failed to load history", e);
|
Logger.getLogger(getClass().getName()).log(Level.SEVERE, "Failed to load history", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +58,7 @@ final class HistorySpooler {
|
||||||
// clear session history
|
// clear session history
|
||||||
sessionHistory.clear();
|
sessionHistory.clear();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Logger.getLogger("global").log(Level.SEVERE, "Failed to store history", e);
|
Logger.getLogger(getClass().getName()).log(Level.SEVERE, "Failed to store history", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,6 @@ public interface MatchFormatter {
|
||||||
public String preview(Match<?, ?> match);
|
public String preview(Match<?, ?> match);
|
||||||
|
|
||||||
|
|
||||||
public String format(Match<?, ?> match);
|
public String format(Match<?, ?> match) throws Exception;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
package net.sourceforge.filebot.ui;
|
package net.sourceforge.filebot.ui.panel.rename;
|
||||||
|
|
||||||
|
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
|
@ -56,6 +56,16 @@ class RenameListCellRenderer extends DefaultFancyListCellRenderer {
|
||||||
typeRenderer.setVisible(false);
|
typeRenderer.setVisible(false);
|
||||||
typeRenderer.setAlpha(1.0f);
|
typeRenderer.setAlpha(1.0f);
|
||||||
|
|
||||||
|
// render unmatched values differently
|
||||||
|
if (!renameModel.hasComplement(index)) {
|
||||||
|
if (isSelected) {
|
||||||
|
setGradientColors(noMatchGradientBeginColor, noMatchGradientEndColor);
|
||||||
|
} else {
|
||||||
|
setForeground(noMatchGradientBeginColor);
|
||||||
|
typeRenderer.setAlpha(0.5f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (value instanceof File) {
|
if (value instanceof File) {
|
||||||
// display file extension
|
// display file extension
|
||||||
File file = (File) value;
|
File file = (File) value;
|
||||||
|
@ -69,9 +79,9 @@ class RenameListCellRenderer extends DefaultFancyListCellRenderer {
|
||||||
}
|
}
|
||||||
} else if (value instanceof FormattedFuture) {
|
} else if (value instanceof FormattedFuture) {
|
||||||
// display progress icon
|
// display progress icon
|
||||||
FormattedFuture future = (FormattedFuture) value;
|
FormattedFuture formattedFuture = (FormattedFuture) value;
|
||||||
|
|
||||||
switch (future.getState()) {
|
switch (formattedFuture.getState()) {
|
||||||
case PENDING:
|
case PENDING:
|
||||||
setIcon(ResourceManager.getIcon("worker.pending"));
|
setIcon(ResourceManager.getIcon("worker.pending"));
|
||||||
break;
|
break;
|
||||||
|
@ -80,15 +90,6 @@ class RenameListCellRenderer extends DefaultFancyListCellRenderer {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!renameModel.hasComplement(index)) {
|
|
||||||
if (isSelected) {
|
|
||||||
setGradientColors(noMatchGradientBeginColor, noMatchGradientEndColor);
|
|
||||||
} else {
|
|
||||||
setForeground(noMatchGradientBeginColor);
|
|
||||||
typeRenderer.setAlpha(0.5f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -10,23 +10,24 @@ import java.util.HashMap;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.logging.Level;
|
import java.util.concurrent.TimeoutException;
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import javax.swing.SwingWorker;
|
import javax.swing.SwingWorker;
|
||||||
import javax.swing.SwingWorker.StateValue;
|
import javax.swing.SwingWorker.StateValue;
|
||||||
|
|
||||||
import net.sourceforge.filebot.similarity.Match;
|
|
||||||
import net.sourceforge.tuned.FileUtilities;
|
|
||||||
import net.sourceforge.tuned.ui.TunedUtilities;
|
|
||||||
import ca.odell.glazedlists.EventList;
|
import ca.odell.glazedlists.EventList;
|
||||||
import ca.odell.glazedlists.TransformedList;
|
import ca.odell.glazedlists.TransformedList;
|
||||||
import ca.odell.glazedlists.event.ListEvent;
|
import ca.odell.glazedlists.event.ListEvent;
|
||||||
|
|
||||||
|
import net.sourceforge.filebot.similarity.Match;
|
||||||
|
import net.sourceforge.tuned.FileUtilities;
|
||||||
|
import net.sourceforge.tuned.ui.TunedUtilities;
|
||||||
|
|
||||||
|
|
||||||
public class RenameModel extends MatchModel<Object, File> {
|
public class RenameModel extends MatchModel<Object, File> {
|
||||||
|
|
||||||
|
@ -82,26 +83,33 @@ public class RenameModel extends MatchModel<Object, File> {
|
||||||
|
|
||||||
for (int i = 0; i < names.size(); i++) {
|
for (int i = 0; i < names.size(); i++) {
|
||||||
if (hasComplement(i)) {
|
if (hasComplement(i)) {
|
||||||
FormattedFuture future = names.get(i);
|
File originalFile = files().get(i);
|
||||||
|
FormattedFuture formattedFuture = names.get(i);
|
||||||
|
|
||||||
// check if background formatter is done
|
StringBuilder nameBuilder = new StringBuilder();
|
||||||
if (!future.isDone()) {
|
|
||||||
throw new IllegalStateException(String.format("\"%s\" has not been formatted yet.", future.toString()));
|
// append formatted name, throw exception if not ready
|
||||||
|
try {
|
||||||
|
nameBuilder.append(formattedFuture.get(0, TimeUnit.SECONDS));
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
throw new IllegalStateException(String.format("\"%s\" could not be formatted: %s.", formattedFuture.preview(), e.getCause().getMessage()));
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
throw new IllegalStateException(String.format("\"%s\" has not been formatted yet.", formattedFuture.preview()));
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
File originalFile = files().get(i);
|
// append extension, if desired
|
||||||
StringBuilder newName = new StringBuilder(future.toString());
|
|
||||||
|
|
||||||
if (preserveExtension) {
|
if (preserveExtension) {
|
||||||
String extension = FileUtilities.getExtension(originalFile);
|
String extension = FileUtilities.getExtension(originalFile);
|
||||||
|
|
||||||
if (extension != null) {
|
if (extension != null) {
|
||||||
newName.append(".").append(extension.toLowerCase());
|
nameBuilder.append('.').append(extension.toLowerCase());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// same parent, different name
|
// same parent, different name
|
||||||
File newFile = new File(originalFile.getParentFile(), newName.toString());
|
File newFile = new File(originalFile.getParentFile(), nameBuilder.toString());
|
||||||
|
|
||||||
// insert mapping
|
// insert mapping
|
||||||
if (map.put(originalFile, newFile) != null) {
|
if (map.put(originalFile, newFile) != null) {
|
||||||
|
@ -278,6 +286,7 @@ public class RenameModel extends MatchModel<Object, File> {
|
||||||
future.cancel(true);
|
future.cancel(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private final PropertyChangeListener futureListener = new PropertyChangeListener() {
|
private final PropertyChangeListener futureListener = new PropertyChangeListener() {
|
||||||
|
|
||||||
public void propertyChange(PropertyChangeEvent evt) {
|
public void propertyChange(PropertyChangeEvent evt) {
|
||||||
|
@ -302,15 +311,10 @@ public class RenameModel extends MatchModel<Object, File> {
|
||||||
|
|
||||||
private final MatchFormatter formatter;
|
private final MatchFormatter formatter;
|
||||||
|
|
||||||
private String display;
|
|
||||||
|
|
||||||
|
|
||||||
private FormattedFuture(Match<Object, File> match, MatchFormatter formatter) {
|
private FormattedFuture(Match<Object, File> match, MatchFormatter formatter) {
|
||||||
this.match = match;
|
this.match = match;
|
||||||
this.formatter = formatter;
|
this.formatter = formatter;
|
||||||
|
|
||||||
// initial display value
|
|
||||||
this.display = formatter.preview(match);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -319,29 +323,29 @@ public class RenameModel extends MatchModel<Object, File> {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String preview() {
|
||||||
|
return formatter.preview(match);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String doInBackground() throws Exception {
|
protected String doInBackground() throws Exception {
|
||||||
return formatter.format(match);
|
return formatter.format(match);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void done() {
|
|
||||||
if (isCancelled()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
this.display = get();
|
|
||||||
} catch (Exception e) {
|
|
||||||
Logger.getLogger("global").log(Level.WARNING, e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return display;
|
if (isDone()) {
|
||||||
|
try {
|
||||||
|
return get(0, TimeUnit.SECONDS);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return String.format("[%s] %s", e instanceof ExecutionException ? e.getCause().getMessage() : e, preview());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// use preview if we are not ready yet
|
||||||
|
return preview();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,6 @@ import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import java.util.prefs.Preferences;
|
import java.util.prefs.Preferences;
|
||||||
|
|
||||||
import javax.script.ScriptException;
|
|
||||||
import javax.swing.AbstractAction;
|
import javax.swing.AbstractAction;
|
||||||
import javax.swing.Action;
|
import javax.swing.Action;
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
|
@ -32,8 +31,8 @@ import ca.odell.glazedlists.swing.EventSelectionModel;
|
||||||
import net.miginfocom.swing.MigLayout;
|
import net.miginfocom.swing.MigLayout;
|
||||||
import net.sourceforge.filebot.ResourceManager;
|
import net.sourceforge.filebot.ResourceManager;
|
||||||
import net.sourceforge.filebot.Settings;
|
import net.sourceforge.filebot.Settings;
|
||||||
|
import net.sourceforge.filebot.format.ExpressionFormat;
|
||||||
import net.sourceforge.filebot.similarity.Match;
|
import net.sourceforge.filebot.similarity.Match;
|
||||||
import net.sourceforge.filebot.ui.EpisodeFormatDialog;
|
|
||||||
import net.sourceforge.filebot.ui.panel.rename.RenameModel.FormattedFuture;
|
import net.sourceforge.filebot.ui.panel.rename.RenameModel.FormattedFuture;
|
||||||
import net.sourceforge.filebot.web.AnidbClient;
|
import net.sourceforge.filebot.web.AnidbClient;
|
||||||
import net.sourceforge.filebot.web.Episode;
|
import net.sourceforge.filebot.web.Episode;
|
||||||
|
@ -159,14 +158,9 @@ public class RenamePanel extends JComponent {
|
||||||
|
|
||||||
switch (dialog.getSelectedOption()) {
|
switch (dialog.getSelectedOption()) {
|
||||||
case APPROVE:
|
case APPROVE:
|
||||||
try {
|
EpisodeExpressionFormatter formatter = new EpisodeExpressionFormatter(dialog.getSelectedFormat());
|
||||||
EpisodeExpressionFormatter formatter = new EpisodeExpressionFormatter(dialog.getExpression());
|
|
||||||
renameModel.useFormatter(Episode.class, formatter);
|
renameModel.useFormatter(Episode.class, formatter);
|
||||||
persistentExpressionFormatter.setValue(formatter);
|
persistentExpressionFormatter.setValue(formatter);
|
||||||
} catch (ScriptException e) {
|
|
||||||
// will not happen because illegal expressions cannot be approved in dialog
|
|
||||||
Logger.getLogger("ui").log(Level.WARNING, e.getMessage(), e);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case USE_DEFAULT:
|
case USE_DEFAULT:
|
||||||
renameModel.useFormatter(Episode.class, null);
|
renameModel.useFormatter(Episode.class, null);
|
||||||
|
@ -325,7 +319,7 @@ public class RenamePanel extends JComponent {
|
||||||
|
|
||||||
if (expression != null) {
|
if (expression != null) {
|
||||||
try {
|
try {
|
||||||
return new EpisodeExpressionFormatter(expression);
|
return new EpisodeExpressionFormatter(new ExpressionFormat(expression));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Logger.getLogger("ui").log(Level.WARNING, e.getMessage(), e);
|
Logger.getLogger("ui").log(Level.WARNING, e.getMessage(), e);
|
||||||
}
|
}
|
||||||
|
@ -337,7 +331,7 @@ public class RenamePanel extends JComponent {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void put(Preferences prefs, String key, EpisodeExpressionFormatter value) {
|
public void put(Preferences prefs, String key, EpisodeExpressionFormatter value) {
|
||||||
prefs.put(key, value.getExpression());
|
prefs.put(key, value.getFormat().getExpression());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -83,16 +83,17 @@ public class AnidbClient implements EpisodeListProvider {
|
||||||
// we might have been redirected to the episode list page
|
// we might have been redirected to the episode list page
|
||||||
if (results.isEmpty()) {
|
if (results.isEmpty()) {
|
||||||
// get anime information from document
|
// get anime information from document
|
||||||
String title = selectTitle(dom);
|
|
||||||
String link = selectString("//*[@class='data']//A[@class='short_link']/@href", dom);
|
String link = selectString("//*[@class='data']//A[@class='short_link']/@href", dom);
|
||||||
|
|
||||||
|
// check if page is an anime page, are an empty search result page
|
||||||
|
if (!link.isEmpty()) {
|
||||||
try {
|
try {
|
||||||
// insert single entry
|
results.add(new HyperLink(selectTitle(dom), new URL(link)));
|
||||||
results.add(new HyperLink(title, new URL(link)));
|
|
||||||
} catch (MalformedURLException e) {
|
} catch (MalformedURLException e) {
|
||||||
Logger.getLogger(getClass().getName()).log(Level.WARNING, "Invalid location: " + link);
|
Logger.getLogger(getClass().getName()).log(Level.WARNING, "Invalid location: " + link);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
@ -100,7 +101,7 @@ public class AnidbClient implements EpisodeListProvider {
|
||||||
|
|
||||||
protected String selectTitle(Document animePage) {
|
protected String selectTitle(Document animePage) {
|
||||||
// extract name from header (e.g. "Anime: Naruto")
|
// extract name from header (e.g. "Anime: Naruto")
|
||||||
return selectString("//H1", animePage).replaceFirst("Anime:\\s*", "");
|
return selectString("//H1", animePage).replaceFirst("^Anime:\\s*", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -71,11 +71,15 @@ public class IMDbClient implements EpisodeListProvider {
|
||||||
|
|
||||||
// we might have been redirected to the movie page
|
// we might have been redirected to the movie page
|
||||||
if (results.isEmpty()) {
|
if (results.isEmpty()) {
|
||||||
|
try {
|
||||||
String name = normalizeName(selectString("//H1/text()", dom));
|
String name = normalizeName(selectString("//H1/text()", dom));
|
||||||
String year = selectString("//H1//A", dom);
|
String year = selectString("//H1//A", dom);
|
||||||
String url = selectString("//LINK[@rel='canonical']/@href", dom);
|
String url = selectString("//LINK[@rel='canonical']/@href", dom);
|
||||||
|
|
||||||
results.add(new MovieDescriptor(name, Integer.parseInt(year), getImdbId(url)));
|
results.add(new MovieDescriptor(name, Integer.parseInt(year), getImdbId(url)));
|
||||||
|
} catch (Exception e) {
|
||||||
|
// ignore, we probably got redirected to an error page
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
|
@ -136,26 +140,14 @@ public class IMDbClient implements EpisodeListProvider {
|
||||||
|
|
||||||
|
|
||||||
protected int getImdbId(String link) {
|
protected int getImdbId(String link) {
|
||||||
try {
|
|
||||||
// try to extract path
|
|
||||||
link = new URI(link).getPath();
|
|
||||||
} catch (URISyntaxException e) {
|
|
||||||
// cannot extract path component, just move on
|
|
||||||
}
|
|
||||||
|
|
||||||
Matcher matcher = Pattern.compile("tt(\\d{7})").matcher(link);
|
Matcher matcher = Pattern.compile("tt(\\d{7})").matcher(link);
|
||||||
|
|
||||||
String imdbId = null;
|
if (matcher.find()) {
|
||||||
|
return Integer.parseInt(matcher.group(1));
|
||||||
// find last match
|
|
||||||
while (matcher.find()) {
|
|
||||||
imdbId = matcher.group(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (imdbId == null)
|
// pattern not found
|
||||||
throw new IllegalArgumentException(String.format("Cannot find imdb id: %s", link));
|
throw new IllegalArgumentException(String.format("Cannot find imdb id: %s", link));
|
||||||
|
|
||||||
return Integer.parseInt(imdbId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,13 +4,15 @@ package net.sourceforge.tuned;
|
||||||
|
|
||||||
import java.util.AbstractList;
|
import java.util.AbstractList;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.RandomAccess;
|
||||||
import java.util.prefs.Preferences;
|
import java.util.prefs.Preferences;
|
||||||
|
|
||||||
import net.sourceforge.tuned.PreferencesMap.Adapter;
|
import net.sourceforge.tuned.PreferencesMap.Adapter;
|
||||||
|
|
||||||
|
|
||||||
public class PreferencesList<T> extends AbstractList<T> {
|
public class PreferencesList<T> extends AbstractList<T> implements RandomAccess {
|
||||||
|
|
||||||
private final PreferencesMap<T> prefs;
|
private final PreferencesMap<T> prefs;
|
||||||
|
|
||||||
|
@ -97,6 +99,25 @@ public class PreferencesList<T> extends AbstractList<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void trimToSize(int limit) {
|
||||||
|
for (int i = size() - 1; i >= limit; i--) {
|
||||||
|
remove(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void set(Collection<T> data) {
|
||||||
|
// remove all elements beyond data.size
|
||||||
|
trimToSize(data.size());
|
||||||
|
|
||||||
|
// override elements
|
||||||
|
int i = 0;
|
||||||
|
for (T element : data) {
|
||||||
|
setImpl(i++, element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clear() {
|
public void clear() {
|
||||||
prefs.clear();
|
prefs.clear();
|
||||||
|
|
|
@ -46,6 +46,14 @@ public class AnidbClientTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void searchNoMatch() throws Exception {
|
||||||
|
List<SearchResult> results = anidb.search("i will not find anything for this query string");
|
||||||
|
|
||||||
|
assertTrue(results.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void searchHideSynonyms() throws Exception {
|
public void searchHideSynonyms() throws Exception {
|
||||||
final List<SearchResult> results = anidb.search("one piece");
|
final List<SearchResult> results = anidb.search("one piece");
|
||||||
|
|
|
@ -28,6 +28,14 @@ public class IMDbClientTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void searchNoMatch() throws Exception {
|
||||||
|
List<SearchResult> results = imdb.search("i will not find anything for this query string");
|
||||||
|
|
||||||
|
assertTrue(results.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void searchResultPageRedirect() throws Exception {
|
public void searchResultPageRedirect() throws Exception {
|
||||||
List<SearchResult> results = imdb.search("my name is earl");
|
List<SearchResult> results = imdb.search("my name is earl");
|
||||||
|
|
|
@ -16,6 +16,8 @@ import org.junit.Test;
|
||||||
|
|
||||||
import net.sourceforge.filebot.web.OpenSubtitlesSubtitleDescriptor.Property;
|
import net.sourceforge.filebot.web.OpenSubtitlesSubtitleDescriptor.Property;
|
||||||
import net.sourceforge.filebot.web.OpenSubtitlesXmlRpc.Query;
|
import net.sourceforge.filebot.web.OpenSubtitlesXmlRpc.Query;
|
||||||
|
import net.sourceforge.filebot.web.OpenSubtitlesXmlRpc.SubFile;
|
||||||
|
import net.sourceforge.filebot.web.OpenSubtitlesXmlRpc.TryUploadResponse;
|
||||||
|
|
||||||
|
|
||||||
public class OpenSubtitlesXmlRpcTest {
|
public class OpenSubtitlesXmlRpcTest {
|
||||||
|
@ -83,6 +85,23 @@ public class OpenSubtitlesXmlRpcTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void tryUploadSubtitles() throws Exception {
|
||||||
|
SubFile subtitle = new SubFile();
|
||||||
|
subtitle.setSubFileName("firefly.s01e01.serenity.pilot.dvdrip.xvid.srt");
|
||||||
|
subtitle.setSubHash("6d9c600fb8b07f87ffcf156e4ed308ca");
|
||||||
|
subtitle.setMovieFileName("firefly.s01e01.serenity.pilot.dvdrip.xvid.avi");
|
||||||
|
subtitle.setMovieHash("2bba5c34b007153b");
|
||||||
|
subtitle.setMovieByteSize(717565952);
|
||||||
|
|
||||||
|
TryUploadResponse response = xmlrpc.tryUploadSubtitles(subtitle);
|
||||||
|
|
||||||
|
assertFalse(response.isUploadRequired());
|
||||||
|
assertEquals("100705", response.getSubtitleData().get(Property.IDSubtitle));
|
||||||
|
assertEquals("eng", response.getSubtitleData().get(Property.SubLanguageID));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void checkSubHash() throws Exception {
|
public void checkSubHash() throws Exception {
|
||||||
Map<String, Integer> subHashMap = xmlrpc.checkSubHash(singleton("e12715f466ee73c86694b7ab9f311285"));
|
Map<String, Integer> subHashMap = xmlrpc.checkSubHash(singleton("e12715f466ee73c86694b7ab9f311285"));
|
||||||
|
|
|
@ -38,6 +38,14 @@ public class TVDotComClientTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void searchNoMatch() throws Exception {
|
||||||
|
List<SearchResult> results = tvdotcom.search("i will not find anything for this query string");
|
||||||
|
|
||||||
|
assertTrue(results.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getEpisodeList() throws Exception {
|
public void getEpisodeList() throws Exception {
|
||||||
List<Episode> list = tvdotcom.getEpisodeList(buffySearchResult, 7);
|
List<Episode> list = tvdotcom.getEpisodeList(buffySearchResult, 7);
|
||||||
|
|
Loading…
Reference in New Issue