1
0
mirror of https://github.com/mitb-archive/filebot synced 2024-08-13 17:03:45 -04:00

* clean release info from any auto-detected series name word sequence

This commit is contained in:
Reinhard Pointner 2011-12-03 10:50:45 +00:00
parent b93e85b9dd
commit 9cb97bf93a
5 changed files with 147 additions and 143 deletions

View File

@ -8,7 +8,7 @@ import static net.sourceforge.filebot.MediaTypes.*;
import static net.sourceforge.filebot.WebServices.*; import static net.sourceforge.filebot.WebServices.*;
import static net.sourceforge.filebot.cli.CLILogging.*; import static net.sourceforge.filebot.cli.CLILogging.*;
import static net.sourceforge.filebot.hash.VerificationUtilities.*; import static net.sourceforge.filebot.hash.VerificationUtilities.*;
import static net.sourceforge.filebot.similarity.SeriesNameMatcher.*; import static net.sourceforge.filebot.mediainfo.ReleaseInfo.*;
import static net.sourceforge.filebot.subtitle.SubtitleUtilities.*; import static net.sourceforge.filebot.subtitle.SubtitleUtilities.*;
import static net.sourceforge.tuned.FileUtilities.*; import static net.sourceforge.tuned.FileUtilities.*;
@ -46,7 +46,6 @@ import net.sourceforge.filebot.format.MediaBindingBean;
import net.sourceforge.filebot.hash.HashType; import net.sourceforge.filebot.hash.HashType;
import net.sourceforge.filebot.hash.VerificationFileReader; import net.sourceforge.filebot.hash.VerificationFileReader;
import net.sourceforge.filebot.hash.VerificationFileWriter; import net.sourceforge.filebot.hash.VerificationFileWriter;
import net.sourceforge.filebot.mediainfo.ReleaseInfo;
import net.sourceforge.filebot.similarity.EpisodeMetrics; import net.sourceforge.filebot.similarity.EpisodeMetrics;
import net.sourceforge.filebot.similarity.Match; import net.sourceforge.filebot.similarity.Match;
import net.sourceforge.filebot.similarity.Matcher; import net.sourceforge.filebot.similarity.Matcher;
@ -98,7 +97,7 @@ public class CmdlineOperations implements CmdlineInterface {
Collection<String> cwsList = emptySet(); Collection<String> cwsList = emptySet();
if (max >= 5) { if (max >= 5) {
cwsList = detectSeriesName(mediaFiles); cwsList = detectSeriesNames(mediaFiles);
} }
SeriesNameMatcher nameMatcher = new SeriesNameMatcher(); SeriesNameMatcher nameMatcher = new SeriesNameMatcher();
@ -557,10 +556,7 @@ public class CmdlineOperations implements CmdlineInterface {
private Collection<String> detectQuery(Collection<File> mediaFiles, boolean strict) throws Exception { private Collection<String> detectQuery(Collection<File> mediaFiles, boolean strict) throws Exception {
Collection<String> names = detectSeriesName(mediaFiles); Collection<String> names = detectSeriesNames(mediaFiles);
// clean detected word sequence from unwanted data
names = new LinkedHashSet<String>(new ReleaseInfo().cleanRG(names));
if (names.isEmpty() || (strict && names.size() > 1)) { if (names.isEmpty() || (strict && names.size() > 1)) {
throw new Exception("Unable to auto-select query: " + names); throw new Exception("Unable to auto-select query: " + names);

View File

@ -12,27 +12,40 @@ import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import net.sourceforge.filebot.similarity.SeriesNameMatcher;
import net.sourceforge.filebot.web.CachedResource; import net.sourceforge.filebot.web.CachedResource;
public class ReleaseInfo { public class ReleaseInfo {
public static Collection<String> detectSeriesNames(Collection<File> files) throws IOException {
SeriesNameMatcher matcher = new SeriesNameMatcher();
ReleaseInfo cleaner = new ReleaseInfo();
// match common word sequence and clean detected word sequence from unwanted elements
Collection<String> names = matcher.matchAll(files.toArray(new File[files.size()]));
return new LinkedHashSet<String>(cleaner.cleanRG(names));
}
public String getVideoSource(File file) { public String getVideoSource(File file) {
// check parent and itself for group names // check parent and itself for group names
return matchLast(getVideoSourcePattern(), file.getParent(), file.getName()); return matchLast(getVideoSourcePattern(), file.getParent(), file.getName());
} }
public String getReleaseGroup(File file) throws IOException { public String getReleaseGroup(File file) throws IOException {
// check parent and itself for group names // check parent and itself for group names
return matchLast(getReleaseGroupPattern(), file.getParent(), file.getName()); return matchLast(getReleaseGroupPattern(), file.getParent(), file.getName());
} }
protected String matchLast(Pattern pattern, CharSequence... sequence) { protected String matchLast(Pattern pattern, CharSequence... sequence) {
String lastMatch = null; String lastMatch = null;
@ -49,27 +62,27 @@ public class ReleaseInfo {
return lastMatch; return lastMatch;
} }
public List<String> clean(Iterable<String> items) throws IOException { public List<String> clean(Iterable<String> items) throws IOException {
return clean(items, getVideoSourcePattern(), getCodecPattern()); return clean(items, getVideoSourcePattern(), getCodecPattern());
} }
public String clean(String item) throws IOException { public String clean(String item) throws IOException {
return clean(item, getVideoSourcePattern(), getCodecPattern()); return clean(item, getVideoSourcePattern(), getCodecPattern());
} }
public List<String> cleanRG(Iterable<String> items) throws IOException { public List<String> cleanRG(Iterable<String> items) throws IOException {
return clean(items, getReleaseGroupPattern(), getVideoSourcePattern(), getCodecPattern()); return clean(items, getReleaseGroupPattern(), getVideoSourcePattern(), getCodecPattern());
} }
public String cleanRG(String item) throws IOException { public String cleanRG(String item) throws IOException {
return clean(item, getReleaseGroupPattern(), getVideoSourcePattern(), getCodecPattern()); return clean(item, getReleaseGroupPattern(), getVideoSourcePattern(), getCodecPattern());
} }
public List<String> clean(Iterable<String> items, Pattern... blacklisted) { public List<String> clean(Iterable<String> items, Pattern... blacklisted) {
List<String> cleanedItems = new ArrayList<String>(); List<String> cleanedItems = new ArrayList<String>();
for (String it : items) { for (String it : items) {
@ -79,7 +92,7 @@ public class ReleaseInfo {
return cleanedItems; return cleanedItems;
} }
public String clean(String item, Pattern... blacklisted) { public String clean(String item, Pattern... blacklisted) {
for (Pattern it : blacklisted) { for (Pattern it : blacklisted) {
item = it.matcher(item).replaceAll(""); item = it.matcher(item).replaceAll("");
@ -88,27 +101,27 @@ public class ReleaseInfo {
return item.replaceAll("[\\p{Punct}\\p{Space}]+", " ").trim(); return item.replaceAll("[\\p{Punct}\\p{Space}]+", " ").trim();
} }
public Pattern getCodecPattern() { public Pattern getCodecPattern() {
// pattern matching any video source name // pattern matching any video source name
String pattern = getBundle(getClass().getName()).getString("pattern.codec"); String pattern = getBundle(getClass().getName()).getString("pattern.codec");
return compile("(?<!\\p{Alnum})(" + pattern + ")(?!\\p{Alnum})", CASE_INSENSITIVE); return compile("(?<!\\p{Alnum})(" + pattern + ")(?!\\p{Alnum})", CASE_INSENSITIVE);
} }
public Pattern getVideoSourcePattern() { public Pattern getVideoSourcePattern() {
// pattern matching any video source name // pattern matching any video source name
String pattern = getBundle(getClass().getName()).getString("pattern.video.source"); String pattern = getBundle(getClass().getName()).getString("pattern.video.source");
return compile("(?<!\\p{Alnum})(" + pattern + ")(?!\\p{Alnum})", CASE_INSENSITIVE); return compile("(?<!\\p{Alnum})(" + pattern + ")(?!\\p{Alnum})", CASE_INSENSITIVE);
} }
public Pattern getReleaseGroupPattern() throws IOException { public Pattern getReleaseGroupPattern() throws IOException {
// pattern matching any release group name enclosed in separators // pattern matching any release group name enclosed in separators
return compile("(?<!\\p{Alnum})(" + join(releaseGroupResource.get(), "|") + ")(?!\\p{Alnum})", CASE_INSENSITIVE); return compile("(?<!\\p{Alnum})(" + join(releaseGroupResource.get(), "|") + ")(?!\\p{Alnum})", CASE_INSENSITIVE);
} }
// fetch release group names online and try to update the data every other day // fetch release group names online and try to update the data every other day
protected final CachedResource<String[]> releaseGroupResource = new CachedResource<String[]>(getBundle(getClass().getName()).getString("url.release-groups"), DAYS.toMillis(2)) { protected final CachedResource<String[]> releaseGroupResource = new CachedResource<String[]>(getBundle(getClass().getName()).getString("url.release-groups"), DAYS.toMillis(2)) {

View File

@ -15,9 +15,9 @@ import java.util.Iterator;
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.Map.Entry;
import java.util.Scanner; import java.util.Scanner;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.Map.Entry;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -28,17 +28,12 @@ import net.sourceforge.tuned.FileUtilities;
public class SeriesNameMatcher { public class SeriesNameMatcher {
public static Collection<String> detectSeriesName(Collection<File> files) {
return new SeriesNameMatcher().matchAll(files.toArray(new File[files.size()]));
}
protected final SeasonEpisodeMatcher seasonEpisodeMatcher = new SeasonEpisodeMatcher(new SeasonEpisodeFilter(30, 50, 1000)); protected final SeasonEpisodeMatcher seasonEpisodeMatcher = new SeasonEpisodeMatcher(new SeasonEpisodeFilter(30, 50, 1000));
protected final NameSimilarityMetric nameSimilarityMetric = new NameSimilarityMetric(); protected final NameSimilarityMetric nameSimilarityMetric = new NameSimilarityMetric();
protected final int commonWordSequenceMaxStartIndex = 3; protected final int commonWordSequenceMaxStartIndex = 3;
public Collection<String> matchAll(File[] files) { public Collection<String> matchAll(File[] files) {
SeriesNameCollection seriesNames = new SeriesNameCollection(); SeriesNameCollection seriesNames = new SeriesNameCollection();
@ -59,7 +54,7 @@ public class SeriesNameMatcher {
return seriesNames; return seriesNames;
} }
public Collection<String> matchAll(String[] names) { public Collection<String> matchAll(String[] names) {
SeriesNameCollection seriesNames = new SeriesNameCollection(); SeriesNameCollection seriesNames = new SeriesNameCollection();
@ -79,7 +74,7 @@ public class SeriesNameMatcher {
return seriesNames; return seriesNames;
} }
/** /**
* Try to match and verify all series names using known season episode patterns. * Try to match and verify all series names using known season episode patterns.
* *
@ -114,7 +109,7 @@ public class SeriesNameMatcher {
return thresholdCollection; return thresholdCollection;
} }
/** /**
* Try to match all common word sequences in the given list. * Try to match all common word sequences in the given list.
* *
@ -144,7 +139,7 @@ public class SeriesNameMatcher {
return results; return results;
} }
/** /**
* Try to match a series name from the given episode name using known season episode * Try to match a series name from the given episode name using known season episode
* patterns. * patterns.
@ -164,7 +159,7 @@ public class SeriesNameMatcher {
return null; return null;
} }
/** /**
* Try to match a series name from the first common word sequence. * Try to match a series name from the first common word sequence.
* *
@ -202,7 +197,7 @@ public class SeriesNameMatcher {
return join(common, " "); return join(common, " ");
} }
protected String normalize(String name) { protected String normalize(String name) {
// remove group names and checksums, any [...] or (...) // remove group names and checksums, any [...] or (...)
name = name.replaceAll("\\([^\\(]*\\)", ""); name = name.replaceAll("\\([^\\(]*\\)", "");
@ -215,7 +210,7 @@ public class SeriesNameMatcher {
return name.trim(); return name.trim();
} }
protected <T> T[] firstCommonSequence(T[] seq1, T[] seq2, int maxStartIndex, Comparator<T> equalsComparator) { protected <T> T[] firstCommonSequence(T[] seq1, T[] seq2, int maxStartIndex, Comparator<T> equalsComparator) {
for (int i = 0; i < seq1.length && i <= maxStartIndex; i++) { for (int i = 0; i < seq1.length && i <= maxStartIndex; i++) {
for (int j = 0; j < seq2.length && j <= maxStartIndex; j++) { for (int j = 0; j < seq2.length && j <= maxStartIndex; j++) {
@ -241,7 +236,7 @@ public class SeriesNameMatcher {
return null; return null;
} }
private Map<File, String[]> mapNamesByFolder(File... files) { private Map<File, String[]> mapNamesByFolder(File... files) {
Map<File, List<File>> filesByFolder = new LinkedHashMap<File, List<File>>(); Map<File, List<File>> filesByFolder = new LinkedHashMap<File, List<File>>();
@ -268,7 +263,7 @@ public class SeriesNameMatcher {
return namesByFolder; return namesByFolder;
} }
protected String[] names(Collection<File> files) { protected String[] names(Collection<File> files) {
String[] names = new String[files.size()]; String[] names = new String[files.size()];
@ -282,12 +277,12 @@ public class SeriesNameMatcher {
return names; return names;
} }
protected static class SeriesNameCollection extends AbstractCollection<String> { protected static class SeriesNameCollection extends AbstractCollection<String> {
private final Map<String, String> data = new LinkedHashMap<String, String>(); private final Map<String, String> data = new LinkedHashMap<String, String>();
@Override @Override
public boolean add(String value) { public boolean add(String value) {
value = value.trim(); value = value.trim();
@ -308,12 +303,12 @@ public class SeriesNameMatcher {
return false; return false;
} }
protected String key(Object value) { protected String key(Object value) {
return value.toString().toLowerCase(); return value.toString().toLowerCase();
} }
protected float firstCharacterCaseBalance(String s) { protected float firstCharacterCaseBalance(String s) {
int upper = 0; int upper = 0;
int lower = 0; int lower = 0;
@ -333,19 +328,19 @@ public class SeriesNameMatcher {
return (lower + (upper * 1.01f)) / Math.abs(lower - upper); return (lower + (upper * 1.01f)) / Math.abs(lower - upper);
} }
@Override @Override
public boolean contains(Object value) { public boolean contains(Object value) {
return data.containsKey(key(value)); return data.containsKey(key(value));
} }
@Override @Override
public Iterator<String> iterator() { public Iterator<String> iterator() {
return data.values().iterator(); return data.values().iterator();
} }
@Override @Override
public int size() { public int size() {
return data.size(); return data.size();
@ -353,7 +348,7 @@ public class SeriesNameMatcher {
} }
protected static class ThresholdCollection<E> extends AbstractCollection<E> { protected static class ThresholdCollection<E> extends AbstractCollection<E> {
private final Collection<E> heaven; private final Collection<E> heaven;
@ -361,14 +356,14 @@ public class SeriesNameMatcher {
private final int threshold; private final int threshold;
public ThresholdCollection(int threshold, Comparator<E> equalityComparator) { public ThresholdCollection(int threshold, Comparator<E> equalityComparator) {
this.heaven = new ArrayList<E>(); this.heaven = new ArrayList<E>();
this.limbo = new TreeMap<E, Collection<E>>(equalityComparator); this.limbo = new TreeMap<E, Collection<E>>(equalityComparator);
this.threshold = threshold; this.threshold = threshold;
} }
@Override @Override
public boolean add(E value) { public boolean add(E value) {
Collection<E> buffer = limbo.get(value); Collection<E> buffer = limbo.get(value);
@ -400,18 +395,18 @@ public class SeriesNameMatcher {
return false; return false;
}; };
public boolean addDirect(E element) { public boolean addDirect(E element) {
return heaven.add(element); return heaven.add(element);
} }
@Override @Override
public Iterator<E> iterator() { public Iterator<E> iterator() {
return heaven.iterator(); return heaven.iterator();
} }
@Override @Override
public int size() { public int size() {
return heaven.size(); return heaven.size();

View File

@ -4,7 +4,7 @@ package net.sourceforge.filebot.ui.rename;
import static java.util.Collections.*; import static java.util.Collections.*;
import static net.sourceforge.filebot.MediaTypes.*; import static net.sourceforge.filebot.MediaTypes.*;
import static net.sourceforge.filebot.similarity.SeriesNameMatcher.*; import static net.sourceforge.filebot.mediainfo.ReleaseInfo.*;
import static net.sourceforge.filebot.web.EpisodeUtilities.*; import static net.sourceforge.filebot.web.EpisodeUtilities.*;
import static net.sourceforge.tuned.FileUtilities.*; import static net.sourceforge.tuned.FileUtilities.*;
import static net.sourceforge.tuned.ui.TunedUtilities.*; import static net.sourceforge.tuned.ui.TunedUtilities.*;
@ -51,12 +51,12 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
private final EpisodeListProvider provider; private final EpisodeListProvider provider;
public EpisodeListMatcher(EpisodeListProvider provider) { public EpisodeListMatcher(EpisodeListProvider provider) {
this.provider = provider; this.provider = provider;
} }
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, final Component parent) throws Exception {
if (searchResults.size() == 1) { if (searchResults.size() == 1) {
return searchResults.get(0); return searchResults.get(0);
@ -111,13 +111,13 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
return showSelectDialog.get(); return showSelectDialog.get();
} }
private String normalizeName(String value) { private String normalizeName(String value) {
// remove trailing braces, e.g. Doctor Who (2005) -> doctor who // remove trailing braces, e.g. Doctor Who (2005) -> doctor who
return removeTrailingBrackets(value).toLowerCase(); return removeTrailingBrackets(value).toLowerCase();
} }
protected Set<Episode> fetchEpisodeSet(Collection<String> seriesNames, final Locale locale, final Component parent) throws Exception { protected Set<Episode> fetchEpisodeSet(Collection<String> seriesNames, 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>>>();
@ -165,7 +165,7 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
} }
} }
@Override @Override
public List<Match<File, ?>> match(final List<File> files, final Locale locale, final boolean autodetection, final Component parent) throws Exception { public List<Match<File, ?>> match(final List<File> files, final Locale locale, final boolean autodetection, final Component parent) throws Exception {
// focus on movie and subtitle files // focus on movie and subtitle files
@ -173,7 +173,7 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
final Map<File, List<File>> filesByFolder = mapByFolder(mediaFiles); final Map<File, List<File>> filesByFolder = mapByFolder(mediaFiles);
// do matching all at once // do matching all at once
if (filesByFolder.keySet().size() <= 5 || detectSeriesName(mediaFiles).size() <= 5) { if (filesByFolder.keySet().size() <= 5 || detectSeriesNames(mediaFiles).size() <= 5) {
return matchEpisodeSet(mediaFiles, locale, autodetection, parent); return matchEpisodeSet(mediaFiles, locale, autodetection, parent);
} }
@ -209,13 +209,13 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
} }
} }
public List<Match<File, ?>> matchEpisodeSet(final List<File> files, Locale locale, boolean autodetection, Component parent) throws Exception { public List<Match<File, ?>> matchEpisodeSet(final List<File> files, Locale locale, boolean autodetection, 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
if (autodetection) { if (autodetection) {
Collection<String> names = detectSeriesName(files); Collection<String> names = detectSeriesNames(files);
if (names.size() > 0) { if (names.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 (provider) { synchronized (provider) {

View File

@ -4,7 +4,7 @@ package net.sourceforge.filebot.ui.subtitle;
import static javax.swing.BorderFactory.*; import static javax.swing.BorderFactory.*;
import static javax.swing.JOptionPane.*; import static javax.swing.JOptionPane.*;
import static net.sourceforge.filebot.similarity.SeriesNameMatcher.*; import static net.sourceforge.filebot.mediainfo.ReleaseInfo.*;
import static net.sourceforge.filebot.subtitle.SubtitleUtilities.*; import static net.sourceforge.filebot.subtitle.SubtitleUtilities.*;
import static net.sourceforge.tuned.FileUtilities.*; import static net.sourceforge.tuned.FileUtilities.*;
import static net.sourceforge.tuned.StringUtilities.*; import static net.sourceforge.tuned.StringUtilities.*;
@ -27,8 +27,8 @@ import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.TreeSet;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.TreeSet;
import java.util.concurrent.CancellationException; import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
@ -87,7 +87,7 @@ class SubtitleAutoMatchDialog extends JDialog {
private ExecutorService queryService; private ExecutorService queryService;
private ExecutorService downloadService; private ExecutorService downloadService;
public SubtitleAutoMatchDialog(Window owner) { public SubtitleAutoMatchDialog(Window owner) {
super(owner, "Download Subtitles", ModalityType.DOCUMENT_MODAL); super(owner, "Download Subtitles", ModalityType.DOCUMENT_MODAL);
@ -102,7 +102,7 @@ class SubtitleAutoMatchDialog extends JDialog {
content.add(new JButton(finishAction), "tag cancel"); content.add(new JButton(finishAction), "tag cancel");
} }
protected JPanel createServicePanel(Color color) { protected JPanel createServicePanel(Color color) {
JPanel panel = new JPanel(new MigLayout("hidemode 3")); JPanel panel = new JPanel(new MigLayout("hidemode 3"));
panel.setBorder(new RoundBorder()); panel.setBorder(new RoundBorder());
@ -112,7 +112,7 @@ class SubtitleAutoMatchDialog extends JDialog {
return panel; return panel;
} }
protected JTable createTable() { protected JTable createTable() {
JTable table = new JTable(new SubtitleMappingTableModel()); JTable table = new JTable(new SubtitleMappingTableModel());
table.setDefaultRenderer(SubtitleMapping.class, new SubtitleMappingOptionRenderer()); table.setDefaultRenderer(SubtitleMapping.class, new SubtitleMappingOptionRenderer());
@ -148,22 +148,22 @@ class SubtitleAutoMatchDialog extends JDialog {
return table; return table;
} }
public void setVideoFiles(File[] videoFiles) { public void setVideoFiles(File[] videoFiles) {
subtitleMappingTable.setModel(new SubtitleMappingTableModel(videoFiles)); subtitleMappingTable.setModel(new SubtitleMappingTableModel(videoFiles));
} }
public void addSubtitleService(VideoHashSubtitleService service) { public void addSubtitleService(VideoHashSubtitleService service) {
addSubtitleService(new VideoHashSubtitleServiceBean(service), hashMatcherServicePanel); addSubtitleService(new VideoHashSubtitleServiceBean(service), hashMatcherServicePanel);
} }
public void addSubtitleService(SubtitleProvider service) { public void addSubtitleService(SubtitleProvider service) {
addSubtitleService(new SubtitleProviderBean(service, this), nameMatcherServicePanel); addSubtitleService(new SubtitleProviderBean(service, this), nameMatcherServicePanel);
} }
protected void addSubtitleService(final SubtitleServiceBean service, final JPanel servicePanel) { protected void addSubtitleService(final SubtitleServiceBean service, final JPanel servicePanel) {
final LinkButton component = new LinkButton(service.getName(), ResourceManager.getIcon("database"), service.getLink()); final LinkButton component = new LinkButton(service.getName(), ResourceManager.getIcon("database"), service.getLink());
component.setVisible(false); component.setVisible(false);
@ -189,11 +189,11 @@ class SubtitleAutoMatchDialog extends JDialog {
servicePanel.add(component); servicePanel.add(component);
} }
// remember last user input // remember last user input
private List<String> userQuery = new ArrayList<String>(); private List<String> userQuery = new ArrayList<String>();
protected List<String> getUserQuery(String suggestion, String title, Component parent) throws Exception { protected List<String> getUserQuery(String suggestion, String title, Component parent) throws Exception {
synchronized (userQuery) { synchronized (userQuery) {
if (userQuery.isEmpty()) { if (userQuery.isEmpty()) {
@ -203,7 +203,7 @@ class SubtitleAutoMatchDialog extends JDialog {
} }
} }
public void startQuery(String languageName) { public void startQuery(String languageName) {
final SubtitleMappingTableModel mappingModel = (SubtitleMappingTableModel) subtitleMappingTable.getModel(); final SubtitleMappingTableModel mappingModel = (SubtitleMappingTableModel) subtitleMappingTable.getModel();
QueryTask queryTask = new QueryTask(services, mappingModel.getVideoFiles(), languageName, SubtitleAutoMatchDialog.this) { QueryTask queryTask = new QueryTask(services, mappingModel.getVideoFiles(), languageName, SubtitleAutoMatchDialog.this) {
@ -232,7 +232,7 @@ class SubtitleAutoMatchDialog extends JDialog {
queryService.submit(queryTask); queryService.submit(queryTask);
} }
private Boolean showConfirmReplaceDialog(List<?> files) { private Boolean showConfirmReplaceDialog(List<?> files) {
JList existingFilesComponent = new JList(files.toArray()) { JList existingFilesComponent = new JList(files.toArray()) {
@ -262,7 +262,7 @@ class SubtitleAutoMatchDialog extends JDialog {
return null; return null;
} }
private final Action downloadAction = new AbstractAction("Download", ResourceManager.getIcon("dialog.continue")) { private final Action downloadAction = new AbstractAction("Download", ResourceManager.getIcon("dialog.continue")) {
@Override @Override
@ -361,17 +361,17 @@ class SubtitleAutoMatchDialog extends JDialog {
} }
}; };
private static class SubtitleMappingOptionRenderer extends DefaultTableCellRenderer { private static class SubtitleMappingOptionRenderer extends DefaultTableCellRenderer {
private final JComboBox optionComboBox = new SimpleComboBox(); private final JComboBox optionComboBox = new SimpleComboBox();
public SubtitleMappingOptionRenderer() { public SubtitleMappingOptionRenderer() {
optionComboBox.setRenderer(new SubtitleOptionRenderer()); optionComboBox.setRenderer(new SubtitleOptionRenderer());
} }
@Override @Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
SubtitleMapping mapping = (SubtitleMapping) value; SubtitleMapping mapping = (SubtitleMapping) value;
@ -413,12 +413,12 @@ class SubtitleAutoMatchDialog extends JDialog {
} }
} }
private static class SubtitleOptionRenderer extends DefaultListCellRenderer { private static class SubtitleOptionRenderer extends DefaultListCellRenderer {
private final Border padding = createEmptyBorder(3, 3, 3, 3); private final Border padding = createEmptyBorder(3, 3, 3, 3);
@Override @Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
super.getListCellRendererComponent(list, null, index, isSelected, cellHasFocus); super.getListCellRendererComponent(list, null, index, isSelected, cellHasFocus);
@ -444,14 +444,14 @@ class SubtitleAutoMatchDialog extends JDialog {
} }
} }
private static class SubtitleMappingTableModel extends AbstractTableModel implements Iterable<SubtitleMapping> { private static class SubtitleMappingTableModel extends AbstractTableModel implements Iterable<SubtitleMapping> {
private final SubtitleMapping[] data; private final SubtitleMapping[] data;
private boolean optionColumnVisible = false; private boolean optionColumnVisible = false;
public SubtitleMappingTableModel(File... videoFiles) { public SubtitleMappingTableModel(File... videoFiles) {
data = new SubtitleMapping[videoFiles.length]; data = new SubtitleMapping[videoFiles.length];
@ -461,7 +461,7 @@ class SubtitleAutoMatchDialog extends JDialog {
} }
} }
public List<File> getVideoFiles() { public List<File> getVideoFiles() {
return new AbstractList<File>() { return new AbstractList<File>() {
@ -470,7 +470,7 @@ class SubtitleAutoMatchDialog extends JDialog {
return data[index].getVideoFile(); return data[index].getVideoFile();
} }
@Override @Override
public int size() { public int size() {
return data.length; return data.length;
@ -478,13 +478,13 @@ class SubtitleAutoMatchDialog extends JDialog {
}; };
} }
@Override @Override
public Iterator<SubtitleMapping> iterator() { public Iterator<SubtitleMapping> iterator() {
return Arrays.asList(data).iterator(); return Arrays.asList(data).iterator();
} }
public void setOptionColumnVisible(boolean optionColumnVisible) { public void setOptionColumnVisible(boolean optionColumnVisible) {
if (this.optionColumnVisible == optionColumnVisible) if (this.optionColumnVisible == optionColumnVisible)
return; return;
@ -495,13 +495,13 @@ class SubtitleAutoMatchDialog extends JDialog {
fireTableStructureChanged(); fireTableStructureChanged();
} }
@Override @Override
public int getColumnCount() { public int getColumnCount() {
return optionColumnVisible ? 2 : 1; return optionColumnVisible ? 2 : 1;
} }
@Override @Override
public String getColumnName(int column) { public String getColumnName(int column) {
switch (column) { switch (column) {
@ -514,13 +514,13 @@ class SubtitleAutoMatchDialog extends JDialog {
return null; return null;
} }
@Override @Override
public int getRowCount() { public int getRowCount() {
return data.length; return data.length;
} }
@Override @Override
public Object getValueAt(int row, int column) { public Object getValueAt(int row, int column) {
switch (column) { switch (column) {
@ -533,19 +533,19 @@ class SubtitleAutoMatchDialog extends JDialog {
return null; return null;
} }
@Override @Override
public void setValueAt(Object value, int row, int column) { public void setValueAt(Object value, int row, int column) {
data[row].setSelectedOption((SubtitleDescriptorBean) value); data[row].setSelectedOption((SubtitleDescriptorBean) value);
} }
@Override @Override
public boolean isCellEditable(int row, int column) { public boolean isCellEditable(int row, int column) {
return column == 1 && data[row].isEditable(); return column == 1 && data[row].isEditable();
} }
@Override @Override
public Class<?> getColumnClass(int column) { public Class<?> getColumnClass(int column) {
switch (column) { switch (column) {
@ -558,17 +558,17 @@ class SubtitleAutoMatchDialog extends JDialog {
return null; return null;
} }
private class SubtitleMappingListener implements PropertyChangeListener { private class SubtitleMappingListener implements PropertyChangeListener {
private final int index; private final int index;
public SubtitleMappingListener(int index) { public SubtitleMappingListener(int index) {
this.index = index; this.index = index;
} }
@Override @Override
public void propertyChange(PropertyChangeEvent evt) { public void propertyChange(PropertyChangeEvent evt) {
// update state and subtitle options // update state and subtitle options
@ -577,7 +577,7 @@ class SubtitleAutoMatchDialog extends JDialog {
} }
} }
private static class SubtitleMapping extends AbstractBean { private static class SubtitleMapping extends AbstractBean {
private File videoFile; private File videoFile;
@ -586,38 +586,38 @@ class SubtitleAutoMatchDialog extends JDialog {
private SubtitleDescriptorBean selectedOption; private SubtitleDescriptorBean selectedOption;
private List<SubtitleDescriptorBean> options = new ArrayList<SubtitleDescriptorBean>(); private List<SubtitleDescriptorBean> options = new ArrayList<SubtitleDescriptorBean>();
public SubtitleMapping(File videoFile) { public SubtitleMapping(File videoFile) {
this.videoFile = videoFile; this.videoFile = videoFile;
} }
public File getVideoFile() { public File getVideoFile() {
return videoFile; return videoFile;
} }
public File getSubtitleFile() { public File getSubtitleFile() {
return subtitleFile; return subtitleFile;
} }
public void setSubtitleFile(File subtitleFile) { public void setSubtitleFile(File subtitleFile) {
this.subtitleFile = subtitleFile; this.subtitleFile = subtitleFile;
firePropertyChange("subtitleFile", null, this.subtitleFile); firePropertyChange("subtitleFile", null, this.subtitleFile);
} }
public boolean isEditable() { public boolean isEditable() {
return subtitleFile == null && selectedOption != null && (selectedOption.getState() == null || selectedOption.getError() != null); return subtitleFile == null && selectedOption != null && (selectedOption.getState() == null || selectedOption.getError() != null);
} }
public SubtitleDescriptorBean getSelectedOption() { public SubtitleDescriptorBean getSelectedOption() {
return selectedOption; return selectedOption;
} }
public void setSelectedOption(SubtitleDescriptorBean selectedOption) { public void setSelectedOption(SubtitleDescriptorBean selectedOption) {
if (this.selectedOption != null) { if (this.selectedOption != null) {
this.selectedOption.removePropertyChangeListener(selectedOptionListener); this.selectedOption.removePropertyChangeListener(selectedOptionListener);
@ -629,12 +629,12 @@ class SubtitleAutoMatchDialog extends JDialog {
firePropertyChange("selectedOption", null, this.selectedOption); firePropertyChange("selectedOption", null, this.selectedOption);
} }
public SubtitleDescriptorBean[] getOptions() { public SubtitleDescriptorBean[] getOptions() {
return options.toArray(new SubtitleDescriptorBean[0]); return options.toArray(new SubtitleDescriptorBean[0]);
} }
public void addOptions(List<SubtitleDescriptorBean> options) { public void addOptions(List<SubtitleDescriptorBean> options) {
this.options.addAll(options); this.options.addAll(options);
@ -643,7 +643,7 @@ class SubtitleAutoMatchDialog extends JDialog {
} }
} }
private final PropertyChangeListener selectedOptionListener = new PropertyChangeListener() { private final PropertyChangeListener selectedOptionListener = new PropertyChangeListener() {
@Override @Override
@ -653,7 +653,7 @@ class SubtitleAutoMatchDialog extends JDialog {
}; };
} }
private static class SubtitleDescriptorBean extends AbstractBean { private static class SubtitleDescriptorBean extends AbstractBean {
private final File videoFile; private final File videoFile;
@ -663,39 +663,39 @@ class SubtitleAutoMatchDialog extends JDialog {
private StateValue state; private StateValue state;
private Exception error; private Exception error;
public SubtitleDescriptorBean(File videoFile, SubtitleDescriptor descriptor, SubtitleServiceBean service) { public SubtitleDescriptorBean(File videoFile, SubtitleDescriptor descriptor, SubtitleServiceBean service) {
this.videoFile = videoFile; this.videoFile = videoFile;
this.descriptor = descriptor; this.descriptor = descriptor;
this.service = service; this.service = service;
} }
public float getMatchProbability() { public float getMatchProbability() {
return service.getMatchProbabilty(videoFile, descriptor); return service.getMatchProbabilty(videoFile, descriptor);
} }
public String getText() { public String getText() {
return formatSubtitle(descriptor.getName(), getLanguageName(), getType()); return formatSubtitle(descriptor.getName(), getLanguageName(), getType());
} }
public Icon getIcon() { public Icon getIcon() {
return service.getIcon(); return service.getIcon();
} }
public String getLanguageName() { public String getLanguageName() {
return descriptor.getLanguageName(); return descriptor.getLanguageName();
} }
public String getType() { public String getType() {
return descriptor.getType(); return descriptor.getType();
} }
public MemoryFile fetch() throws Exception { public MemoryFile fetch() throws Exception {
setState(StateValue.STARTED); setState(StateValue.STARTED);
@ -715,30 +715,30 @@ class SubtitleAutoMatchDialog extends JDialog {
} }
} }
public Exception getError() { public Exception getError() {
return error; return error;
} }
public StateValue getState() { public StateValue getState() {
return state; return state;
} }
public void setState(StateValue state) { public void setState(StateValue state) {
this.state = state; this.state = state;
firePropertyChange("state", null, this.state); firePropertyChange("state", null, this.state);
} }
@Override @Override
public String toString() { public String toString() {
return getText(); return getText();
} }
} }
private static class QueryTask extends SwingWorker<Collection<File>, Map<File, List<SubtitleDescriptorBean>>> { private static class QueryTask extends SwingWorker<Collection<File>, Map<File, List<SubtitleDescriptorBean>>> {
private final Component parent; private final Component parent;
@ -747,7 +747,7 @@ class SubtitleAutoMatchDialog extends JDialog {
private final Collection<File> remainingVideos; private final Collection<File> remainingVideos;
private final String languageName; private final String languageName;
public QueryTask(Collection<SubtitleServiceBean> services, Collection<File> videoFiles, String languageName, Component parent) { public QueryTask(Collection<SubtitleServiceBean> services, Collection<File> videoFiles, String languageName, Component parent) {
this.parent = parent; this.parent = parent;
this.services = services; this.services = services;
@ -755,7 +755,7 @@ class SubtitleAutoMatchDialog extends JDialog {
this.languageName = languageName; this.languageName = languageName;
} }
@Override @Override
protected Collection<File> doInBackground() throws Exception { protected Collection<File> doInBackground() throws Exception {
for (SubtitleServiceBean service : services) { for (SubtitleServiceBean service : services) {
@ -804,24 +804,24 @@ class SubtitleAutoMatchDialog extends JDialog {
} }
} }
private static class DownloadTask extends SwingWorker<File, Void> { private static class DownloadTask extends SwingWorker<File, Void> {
private final File video; private final File video;
private final SubtitleDescriptorBean descriptor; private final SubtitleDescriptorBean descriptor;
public DownloadTask(File video, SubtitleDescriptorBean descriptor) { public DownloadTask(File video, SubtitleDescriptorBean descriptor) {
this.video = video; this.video = video;
this.descriptor = descriptor; this.descriptor = descriptor;
} }
public SubtitleDescriptorBean getSubtitleBean() { public SubtitleDescriptorBean getSubtitleBean() {
return descriptor; return descriptor;
} }
public File getDestination(MemoryFile subtitle) { public File getDestination(MemoryFile subtitle) {
if (descriptor.getType() == null && subtitle == null) if (descriptor.getType() == null && subtitle == null)
return null; return null;
@ -832,7 +832,7 @@ class SubtitleAutoMatchDialog extends JDialog {
return new File(video.getParentFile(), formatSubtitle(base, descriptor.getLanguageName(), ext)); return new File(video.getParentFile(), formatSubtitle(base, descriptor.getLanguageName(), ext));
} }
@Override @Override
protected File doInBackground() { protected File doInBackground() {
try { try {
@ -855,7 +855,7 @@ class SubtitleAutoMatchDialog extends JDialog {
} }
} }
protected static abstract class SubtitleServiceBean extends AbstractBean { protected static abstract class SubtitleServiceBean extends AbstractBean {
private final String name; private final String name;
@ -865,35 +865,35 @@ class SubtitleAutoMatchDialog extends JDialog {
private StateValue state = StateValue.PENDING; private StateValue state = StateValue.PENDING;
private Throwable error = null; private Throwable error = null;
public SubtitleServiceBean(String name, Icon icon, URI link) { public SubtitleServiceBean(String name, Icon icon, URI link) {
this.name = name; this.name = name;
this.icon = icon; this.icon = icon;
this.link = link; this.link = link;
} }
public String getName() { public String getName() {
return name; return name;
} }
public Icon getIcon() { public Icon getIcon() {
return icon; return icon;
} }
public URI getLink() { public URI getLink() {
return link; return link;
} }
public abstract float getMatchProbabilty(File videoFile, SubtitleDescriptor descriptor); public abstract float getMatchProbabilty(File videoFile, SubtitleDescriptor descriptor);
protected abstract Map<File, List<SubtitleDescriptor>> getSubtitleList(Collection<File> files, String languageName, Component parent) throws Exception; protected abstract Map<File, List<SubtitleDescriptor>> getSubtitleList(Collection<File> files, String languageName, Component parent) throws Exception;
public final Map<File, List<SubtitleDescriptor>> lookupSubtitles(Collection<File> files, String languageName, Component parent) throws Exception { public final Map<File, List<SubtitleDescriptor>> lookupSubtitles(Collection<File> files, String languageName, Component parent) throws Exception {
setState(StateValue.STARTED); setState(StateValue.STARTED);
@ -910,61 +910,61 @@ class SubtitleAutoMatchDialog extends JDialog {
} }
} }
private void setState(StateValue state) { private void setState(StateValue state) {
this.state = state; this.state = state;
firePropertyChange("state", null, this.state); firePropertyChange("state", null, this.state);
} }
public StateValue getState() { public StateValue getState() {
return state; return state;
} }
public Throwable getError() { public Throwable getError() {
return error; return error;
} }
} }
protected static class VideoHashSubtitleServiceBean extends SubtitleServiceBean { protected static class VideoHashSubtitleServiceBean extends SubtitleServiceBean {
private VideoHashSubtitleService service; private VideoHashSubtitleService service;
public VideoHashSubtitleServiceBean(VideoHashSubtitleService service) { public VideoHashSubtitleServiceBean(VideoHashSubtitleService service) {
super(service.getName(), service.getIcon(), service.getLink()); super(service.getName(), service.getIcon(), service.getLink());
this.service = service; this.service = service;
} }
@Override @Override
protected Map<File, List<SubtitleDescriptor>> getSubtitleList(Collection<File> files, String languageName, Component parent) throws Exception { protected Map<File, List<SubtitleDescriptor>> getSubtitleList(Collection<File> files, String languageName, Component parent) throws Exception {
return service.getSubtitleList(files.toArray(new File[0]), languageName); return service.getSubtitleList(files.toArray(new File[0]), languageName);
} }
@Override @Override
public float getMatchProbabilty(File videoFile, SubtitleDescriptor descriptor) { public float getMatchProbabilty(File videoFile, SubtitleDescriptor descriptor) {
return 1; return 1;
} }
} }
protected static class SubtitleProviderBean extends SubtitleServiceBean { protected static class SubtitleProviderBean extends SubtitleServiceBean {
private SubtitleAutoMatchDialog inputProvider; private SubtitleAutoMatchDialog inputProvider;
private SubtitleProvider service; private SubtitleProvider service;
public SubtitleProviderBean(SubtitleProvider service, SubtitleAutoMatchDialog inputProvider) { public SubtitleProviderBean(SubtitleProvider service, SubtitleAutoMatchDialog inputProvider) {
super(service.getName(), service.getIcon(), service.getLink()); super(service.getName(), service.getIcon(), service.getLink());
this.service = service; this.service = service;
this.inputProvider = inputProvider; this.inputProvider = inputProvider;
} }
@Override @Override
protected Map<File, List<SubtitleDescriptor>> getSubtitleList(Collection<File> files, String languageName, Component parent) throws Exception { protected Map<File, List<SubtitleDescriptor>> getSubtitleList(Collection<File> files, String languageName, Component parent) throws Exception {
Map<File, List<SubtitleDescriptor>> subtitlesByFile = new HashMap<File, List<SubtitleDescriptor>>(); Map<File, List<SubtitleDescriptor>> subtitlesByFile = new HashMap<File, List<SubtitleDescriptor>>();
@ -973,7 +973,7 @@ class SubtitleAutoMatchDialog extends JDialog {
} }
// auto-detect query and search for subtitles // auto-detect query and search for subtitles
Collection<String> querySet = detectSeriesName(files); Collection<String> querySet = detectSeriesNames(files);
List<SubtitleDescriptor> subtitles = findSubtitles(service, querySet, languageName); List<SubtitleDescriptor> subtitles = findSubtitles(service, querySet, languageName);
// if auto-detection fails, ask user for input // if auto-detection fails, ask user for input
@ -1016,7 +1016,7 @@ class SubtitleAutoMatchDialog extends JDialog {
return subtitlesByFile; return subtitlesByFile;
} }
@Override @Override
public float getMatchProbabilty(File videoFile, SubtitleDescriptor descriptor) { public float getMatchProbabilty(File videoFile, SubtitleDescriptor descriptor) {
return EpisodeMetrics.verificationMetric().getSimilarity(videoFile, descriptor) * 0.9f; return EpisodeMetrics.verificationMetric().getSimilarity(videoFile, descriptor) * 0.9f;