* clean release info from any auto-detected series name word sequence
This commit is contained in:
parent
b93e85b9dd
commit
9cb97bf93a
|
@ -8,7 +8,7 @@ import static net.sourceforge.filebot.MediaTypes.*;
|
|||
import static net.sourceforge.filebot.WebServices.*;
|
||||
import static net.sourceforge.filebot.cli.CLILogging.*;
|
||||
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.tuned.FileUtilities.*;
|
||||
|
||||
|
@ -46,7 +46,6 @@ import net.sourceforge.filebot.format.MediaBindingBean;
|
|||
import net.sourceforge.filebot.hash.HashType;
|
||||
import net.sourceforge.filebot.hash.VerificationFileReader;
|
||||
import net.sourceforge.filebot.hash.VerificationFileWriter;
|
||||
import net.sourceforge.filebot.mediainfo.ReleaseInfo;
|
||||
import net.sourceforge.filebot.similarity.EpisodeMetrics;
|
||||
import net.sourceforge.filebot.similarity.Match;
|
||||
import net.sourceforge.filebot.similarity.Matcher;
|
||||
|
@ -98,7 +97,7 @@ public class CmdlineOperations implements CmdlineInterface {
|
|||
|
||||
Collection<String> cwsList = emptySet();
|
||||
if (max >= 5) {
|
||||
cwsList = detectSeriesName(mediaFiles);
|
||||
cwsList = detectSeriesNames(mediaFiles);
|
||||
}
|
||||
|
||||
SeriesNameMatcher nameMatcher = new SeriesNameMatcher();
|
||||
|
@ -557,10 +556,7 @@ public class CmdlineOperations implements CmdlineInterface {
|
|||
|
||||
|
||||
private Collection<String> detectQuery(Collection<File> mediaFiles, boolean strict) throws Exception {
|
||||
Collection<String> names = detectSeriesName(mediaFiles);
|
||||
|
||||
// clean detected word sequence from unwanted data
|
||||
names = new LinkedHashSet<String>(new ReleaseInfo().cleanRG(names));
|
||||
Collection<String> names = detectSeriesNames(mediaFiles);
|
||||
|
||||
if (names.isEmpty() || (strict && names.size() > 1)) {
|
||||
throw new Exception("Unable to auto-select query: " + names);
|
||||
|
|
|
@ -12,27 +12,40 @@ import java.io.IOException;
|
|||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import net.sourceforge.filebot.similarity.SeriesNameMatcher;
|
||||
import net.sourceforge.filebot.web.CachedResource;
|
||||
|
||||
|
||||
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) {
|
||||
// check parent and itself for group names
|
||||
return matchLast(getVideoSourcePattern(), file.getParent(), file.getName());
|
||||
}
|
||||
|
||||
|
||||
|
||||
public String getReleaseGroup(File file) throws IOException {
|
||||
// check parent and itself for group names
|
||||
return matchLast(getReleaseGroupPattern(), file.getParent(), file.getName());
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected String matchLast(Pattern pattern, CharSequence... sequence) {
|
||||
String lastMatch = null;
|
||||
|
||||
|
@ -49,27 +62,27 @@ public class ReleaseInfo {
|
|||
return lastMatch;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public List<String> clean(Iterable<String> items) throws IOException {
|
||||
return clean(items, getVideoSourcePattern(), getCodecPattern());
|
||||
}
|
||||
|
||||
|
||||
|
||||
public String clean(String item) throws IOException {
|
||||
return clean(item, getVideoSourcePattern(), getCodecPattern());
|
||||
}
|
||||
|
||||
|
||||
|
||||
public List<String> cleanRG(Iterable<String> items) throws IOException {
|
||||
return clean(items, getReleaseGroupPattern(), getVideoSourcePattern(), getCodecPattern());
|
||||
}
|
||||
|
||||
|
||||
|
||||
public String cleanRG(String item) throws IOException {
|
||||
return clean(item, getReleaseGroupPattern(), getVideoSourcePattern(), getCodecPattern());
|
||||
}
|
||||
|
||||
|
||||
|
||||
public List<String> clean(Iterable<String> items, Pattern... blacklisted) {
|
||||
List<String> cleanedItems = new ArrayList<String>();
|
||||
for (String it : items) {
|
||||
|
@ -79,7 +92,7 @@ public class ReleaseInfo {
|
|||
return cleanedItems;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public String clean(String item, Pattern... blacklisted) {
|
||||
for (Pattern it : blacklisted) {
|
||||
item = it.matcher(item).replaceAll("");
|
||||
|
@ -88,27 +101,27 @@ public class ReleaseInfo {
|
|||
return item.replaceAll("[\\p{Punct}\\p{Space}]+", " ").trim();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public Pattern getCodecPattern() {
|
||||
// pattern matching any video source name
|
||||
String pattern = getBundle(getClass().getName()).getString("pattern.codec");
|
||||
return compile("(?<!\\p{Alnum})(" + pattern + ")(?!\\p{Alnum})", CASE_INSENSITIVE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public Pattern getVideoSourcePattern() {
|
||||
// pattern matching any video source name
|
||||
String pattern = getBundle(getClass().getName()).getString("pattern.video.source");
|
||||
return compile("(?<!\\p{Alnum})(" + pattern + ")(?!\\p{Alnum})", CASE_INSENSITIVE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public Pattern getReleaseGroupPattern() throws IOException {
|
||||
// pattern matching any release group name enclosed in separators
|
||||
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
|
||||
protected final CachedResource<String[]> releaseGroupResource = new CachedResource<String[]>(getBundle(getClass().getName()).getString("url.release-groups"), DAYS.toMillis(2)) {
|
||||
|
||||
|
|
|
@ -15,9 +15,9 @@ import java.util.Iterator;
|
|||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Scanner;
|
||||
import java.util.TreeMap;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
@ -28,17 +28,12 @@ import net.sourceforge.tuned.FileUtilities;
|
|||
|
||||
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 NameSimilarityMetric nameSimilarityMetric = new NameSimilarityMetric();
|
||||
|
||||
protected final int commonWordSequenceMaxStartIndex = 3;
|
||||
|
||||
|
||||
|
||||
public Collection<String> matchAll(File[] files) {
|
||||
SeriesNameCollection seriesNames = new SeriesNameCollection();
|
||||
|
||||
|
@ -59,7 +54,7 @@ public class SeriesNameMatcher {
|
|||
return seriesNames;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public Collection<String> matchAll(String[] names) {
|
||||
SeriesNameCollection seriesNames = new SeriesNameCollection();
|
||||
|
||||
|
@ -79,7 +74,7 @@ public class SeriesNameMatcher {
|
|||
return seriesNames;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Try to match and verify all series names using known season episode patterns.
|
||||
*
|
||||
|
@ -114,7 +109,7 @@ public class SeriesNameMatcher {
|
|||
return thresholdCollection;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Try to match all common word sequences in the given list.
|
||||
*
|
||||
|
@ -144,7 +139,7 @@ public class SeriesNameMatcher {
|
|||
return results;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Try to match a series name from the given episode name using known season episode
|
||||
* patterns.
|
||||
|
@ -164,7 +159,7 @@ public class SeriesNameMatcher {
|
|||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Try to match a series name from the first common word sequence.
|
||||
*
|
||||
|
@ -202,7 +197,7 @@ public class SeriesNameMatcher {
|
|||
return join(common, " ");
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected String normalize(String name) {
|
||||
// remove group names and checksums, any [...] or (...)
|
||||
name = name.replaceAll("\\([^\\(]*\\)", "");
|
||||
|
@ -215,7 +210,7 @@ public class SeriesNameMatcher {
|
|||
return name.trim();
|
||||
}
|
||||
|
||||
|
||||
|
||||
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 j = 0; j < seq2.length && j <= maxStartIndex; j++) {
|
||||
|
@ -241,7 +236,7 @@ public class SeriesNameMatcher {
|
|||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private Map<File, String[]> mapNamesByFolder(File... files) {
|
||||
Map<File, List<File>> filesByFolder = new LinkedHashMap<File, List<File>>();
|
||||
|
||||
|
@ -268,7 +263,7 @@ public class SeriesNameMatcher {
|
|||
return namesByFolder;
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected String[] names(Collection<File> files) {
|
||||
String[] names = new String[files.size()];
|
||||
|
||||
|
@ -282,12 +277,12 @@ public class SeriesNameMatcher {
|
|||
return names;
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected static class SeriesNameCollection extends AbstractCollection<String> {
|
||||
|
||||
private final Map<String, String> data = new LinkedHashMap<String, String>();
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean add(String value) {
|
||||
value = value.trim();
|
||||
|
@ -308,12 +303,12 @@ public class SeriesNameMatcher {
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected String key(Object value) {
|
||||
return value.toString().toLowerCase();
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected float firstCharacterCaseBalance(String s) {
|
||||
int upper = 0;
|
||||
int lower = 0;
|
||||
|
@ -333,19 +328,19 @@ public class SeriesNameMatcher {
|
|||
return (lower + (upper * 1.01f)) / Math.abs(lower - upper);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean contains(Object value) {
|
||||
return data.containsKey(key(value));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public Iterator<String> iterator() {
|
||||
return data.values().iterator();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return data.size();
|
||||
|
@ -353,7 +348,7 @@ public class SeriesNameMatcher {
|
|||
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected static class ThresholdCollection<E> extends AbstractCollection<E> {
|
||||
|
||||
private final Collection<E> heaven;
|
||||
|
@ -361,14 +356,14 @@ public class SeriesNameMatcher {
|
|||
|
||||
private final int threshold;
|
||||
|
||||
|
||||
|
||||
public ThresholdCollection(int threshold, Comparator<E> equalityComparator) {
|
||||
this.heaven = new ArrayList<E>();
|
||||
this.limbo = new TreeMap<E, Collection<E>>(equalityComparator);
|
||||
this.threshold = threshold;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean add(E value) {
|
||||
Collection<E> buffer = limbo.get(value);
|
||||
|
@ -400,18 +395,18 @@ public class SeriesNameMatcher {
|
|||
return false;
|
||||
};
|
||||
|
||||
|
||||
|
||||
public boolean addDirect(E element) {
|
||||
return heaven.add(element);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return heaven.iterator();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return heaven.size();
|
||||
|
|
|
@ -4,7 +4,7 @@ package net.sourceforge.filebot.ui.rename;
|
|||
|
||||
import static java.util.Collections.*;
|
||||
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.tuned.FileUtilities.*;
|
||||
import static net.sourceforge.tuned.ui.TunedUtilities.*;
|
||||
|
@ -51,12 +51,12 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
|
|||
|
||||
private final EpisodeListProvider provider;
|
||||
|
||||
|
||||
|
||||
public EpisodeListMatcher(EpisodeListProvider provider) {
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected SearchResult selectSearchResult(final String query, final List<SearchResult> searchResults, final Component parent) throws Exception {
|
||||
if (searchResults.size() == 1) {
|
||||
return searchResults.get(0);
|
||||
|
@ -111,13 +111,13 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
|
|||
return showSelectDialog.get();
|
||||
}
|
||||
|
||||
|
||||
|
||||
private String normalizeName(String value) {
|
||||
// remove trailing braces, e.g. Doctor Who (2005) -> doctor who
|
||||
return removeTrailingBrackets(value).toLowerCase();
|
||||
}
|
||||
|
||||
|
||||
|
||||
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>>>();
|
||||
|
||||
|
@ -165,7 +165,7 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
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
|
||||
|
@ -173,7 +173,7 @@ class EpisodeListMatcher implements AutoCompleteMatcher {
|
|||
final Map<File, List<File>> filesByFolder = mapByFolder(mediaFiles);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
Set<Episode> episodes = emptySet();
|
||||
|
||||
// detect series name and fetch episode list
|
||||
if (autodetection) {
|
||||
Collection<String> names = detectSeriesName(files);
|
||||
Collection<String> names = detectSeriesNames(files);
|
||||
if (names.size() > 0) {
|
||||
// only allow one fetch session at a time so later requests can make use of cached results
|
||||
synchronized (provider) {
|
||||
|
|
|
@ -4,7 +4,7 @@ package net.sourceforge.filebot.ui.subtitle;
|
|||
|
||||
import static javax.swing.BorderFactory.*;
|
||||
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.tuned.FileUtilities.*;
|
||||
import static net.sourceforge.tuned.StringUtilities.*;
|
||||
|
@ -27,8 +27,8 @@ import java.util.HashMap;
|
|||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeSet;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
@ -87,7 +87,7 @@ class SubtitleAutoMatchDialog extends JDialog {
|
|||
private ExecutorService queryService;
|
||||
private ExecutorService downloadService;
|
||||
|
||||
|
||||
|
||||
public SubtitleAutoMatchDialog(Window owner) {
|
||||
super(owner, "Download Subtitles", ModalityType.DOCUMENT_MODAL);
|
||||
|
||||
|
@ -102,7 +102,7 @@ class SubtitleAutoMatchDialog extends JDialog {
|
|||
content.add(new JButton(finishAction), "tag cancel");
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected JPanel createServicePanel(Color color) {
|
||||
JPanel panel = new JPanel(new MigLayout("hidemode 3"));
|
||||
panel.setBorder(new RoundBorder());
|
||||
|
@ -112,7 +112,7 @@ class SubtitleAutoMatchDialog extends JDialog {
|
|||
return panel;
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected JTable createTable() {
|
||||
JTable table = new JTable(new SubtitleMappingTableModel());
|
||||
table.setDefaultRenderer(SubtitleMapping.class, new SubtitleMappingOptionRenderer());
|
||||
|
@ -148,22 +148,22 @@ class SubtitleAutoMatchDialog extends JDialog {
|
|||
return table;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void setVideoFiles(File[] videoFiles) {
|
||||
subtitleMappingTable.setModel(new SubtitleMappingTableModel(videoFiles));
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void addSubtitleService(VideoHashSubtitleService service) {
|
||||
addSubtitleService(new VideoHashSubtitleServiceBean(service), hashMatcherServicePanel);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void addSubtitleService(SubtitleProvider service) {
|
||||
addSubtitleService(new SubtitleProviderBean(service, this), nameMatcherServicePanel);
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected void addSubtitleService(final SubtitleServiceBean service, final JPanel servicePanel) {
|
||||
final LinkButton component = new LinkButton(service.getName(), ResourceManager.getIcon("database"), service.getLink());
|
||||
component.setVisible(false);
|
||||
|
@ -189,11 +189,11 @@ class SubtitleAutoMatchDialog extends JDialog {
|
|||
servicePanel.add(component);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// remember last user input
|
||||
private List<String> userQuery = new ArrayList<String>();
|
||||
|
||||
|
||||
|
||||
protected List<String> getUserQuery(String suggestion, String title, Component parent) throws Exception {
|
||||
synchronized (userQuery) {
|
||||
if (userQuery.isEmpty()) {
|
||||
|
@ -203,7 +203,7 @@ class SubtitleAutoMatchDialog extends JDialog {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void startQuery(String languageName) {
|
||||
final SubtitleMappingTableModel mappingModel = (SubtitleMappingTableModel) subtitleMappingTable.getModel();
|
||||
QueryTask queryTask = new QueryTask(services, mappingModel.getVideoFiles(), languageName, SubtitleAutoMatchDialog.this) {
|
||||
|
@ -232,7 +232,7 @@ class SubtitleAutoMatchDialog extends JDialog {
|
|||
queryService.submit(queryTask);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private Boolean showConfirmReplaceDialog(List<?> files) {
|
||||
JList existingFilesComponent = new JList(files.toArray()) {
|
||||
|
||||
|
@ -262,7 +262,7 @@ class SubtitleAutoMatchDialog extends JDialog {
|
|||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private final Action downloadAction = new AbstractAction("Download", ResourceManager.getIcon("dialog.continue")) {
|
||||
|
||||
@Override
|
||||
|
@ -361,17 +361,17 @@ class SubtitleAutoMatchDialog extends JDialog {
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
private static class SubtitleMappingOptionRenderer extends DefaultTableCellRenderer {
|
||||
|
||||
private final JComboBox optionComboBox = new SimpleComboBox();
|
||||
|
||||
|
||||
|
||||
public SubtitleMappingOptionRenderer() {
|
||||
optionComboBox.setRenderer(new SubtitleOptionRenderer());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
|
||||
SubtitleMapping mapping = (SubtitleMapping) value;
|
||||
|
@ -413,12 +413,12 @@ class SubtitleAutoMatchDialog extends JDialog {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static class SubtitleOptionRenderer extends DefaultListCellRenderer {
|
||||
|
||||
private final Border padding = createEmptyBorder(3, 3, 3, 3);
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean 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 final SubtitleMapping[] data;
|
||||
|
||||
private boolean optionColumnVisible = false;
|
||||
|
||||
|
||||
|
||||
public SubtitleMappingTableModel(File... videoFiles) {
|
||||
data = new SubtitleMapping[videoFiles.length];
|
||||
|
||||
|
@ -461,7 +461,7 @@ class SubtitleAutoMatchDialog extends JDialog {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public List<File> getVideoFiles() {
|
||||
return new AbstractList<File>() {
|
||||
|
||||
|
@ -470,7 +470,7 @@ class SubtitleAutoMatchDialog extends JDialog {
|
|||
return data[index].getVideoFile();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return data.length;
|
||||
|
@ -478,13 +478,13 @@ class SubtitleAutoMatchDialog extends JDialog {
|
|||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public Iterator<SubtitleMapping> iterator() {
|
||||
return Arrays.asList(data).iterator();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void setOptionColumnVisible(boolean optionColumnVisible) {
|
||||
if (this.optionColumnVisible == optionColumnVisible)
|
||||
return;
|
||||
|
@ -495,13 +495,13 @@ class SubtitleAutoMatchDialog extends JDialog {
|
|||
fireTableStructureChanged();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
return optionColumnVisible ? 2 : 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String getColumnName(int column) {
|
||||
switch (column) {
|
||||
|
@ -514,13 +514,13 @@ class SubtitleAutoMatchDialog extends JDialog {
|
|||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
return data.length;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public Object getValueAt(int row, int column) {
|
||||
switch (column) {
|
||||
|
@ -533,19 +533,19 @@ class SubtitleAutoMatchDialog extends JDialog {
|
|||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void setValueAt(Object value, int row, int column) {
|
||||
data[row].setSelectedOption((SubtitleDescriptorBean) value);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isCellEditable(int row, int column) {
|
||||
return column == 1 && data[row].isEditable();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public Class<?> getColumnClass(int column) {
|
||||
switch (column) {
|
||||
|
@ -558,17 +558,17 @@ class SubtitleAutoMatchDialog extends JDialog {
|
|||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private class SubtitleMappingListener implements PropertyChangeListener {
|
||||
|
||||
private final int index;
|
||||
|
||||
|
||||
|
||||
public SubtitleMappingListener(int index) {
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
// update state and subtitle options
|
||||
|
@ -577,7 +577,7 @@ class SubtitleAutoMatchDialog extends JDialog {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static class SubtitleMapping extends AbstractBean {
|
||||
|
||||
private File videoFile;
|
||||
|
@ -586,38 +586,38 @@ class SubtitleAutoMatchDialog extends JDialog {
|
|||
private SubtitleDescriptorBean selectedOption;
|
||||
private List<SubtitleDescriptorBean> options = new ArrayList<SubtitleDescriptorBean>();
|
||||
|
||||
|
||||
|
||||
public SubtitleMapping(File videoFile) {
|
||||
this.videoFile = videoFile;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public File getVideoFile() {
|
||||
return videoFile;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public File getSubtitleFile() {
|
||||
return subtitleFile;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void setSubtitleFile(File subtitleFile) {
|
||||
this.subtitleFile = subtitleFile;
|
||||
firePropertyChange("subtitleFile", null, this.subtitleFile);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public boolean isEditable() {
|
||||
return subtitleFile == null && selectedOption != null && (selectedOption.getState() == null || selectedOption.getError() != null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public SubtitleDescriptorBean getSelectedOption() {
|
||||
return selectedOption;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void setSelectedOption(SubtitleDescriptorBean selectedOption) {
|
||||
if (this.selectedOption != null) {
|
||||
this.selectedOption.removePropertyChangeListener(selectedOptionListener);
|
||||
|
@ -629,12 +629,12 @@ class SubtitleAutoMatchDialog extends JDialog {
|
|||
firePropertyChange("selectedOption", null, this.selectedOption);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public SubtitleDescriptorBean[] getOptions() {
|
||||
return options.toArray(new SubtitleDescriptorBean[0]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void addOptions(List<SubtitleDescriptorBean> options) {
|
||||
this.options.addAll(options);
|
||||
|
||||
|
@ -643,7 +643,7 @@ class SubtitleAutoMatchDialog extends JDialog {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private final PropertyChangeListener selectedOptionListener = new PropertyChangeListener() {
|
||||
|
||||
@Override
|
||||
|
@ -653,7 +653,7 @@ class SubtitleAutoMatchDialog extends JDialog {
|
|||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static class SubtitleDescriptorBean extends AbstractBean {
|
||||
|
||||
private final File videoFile;
|
||||
|
@ -663,39 +663,39 @@ class SubtitleAutoMatchDialog extends JDialog {
|
|||
private StateValue state;
|
||||
private Exception error;
|
||||
|
||||
|
||||
|
||||
public SubtitleDescriptorBean(File videoFile, SubtitleDescriptor descriptor, SubtitleServiceBean service) {
|
||||
this.videoFile = videoFile;
|
||||
this.descriptor = descriptor;
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public float getMatchProbability() {
|
||||
return service.getMatchProbabilty(videoFile, descriptor);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public String getText() {
|
||||
return formatSubtitle(descriptor.getName(), getLanguageName(), getType());
|
||||
}
|
||||
|
||||
|
||||
|
||||
public Icon getIcon() {
|
||||
return service.getIcon();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public String getLanguageName() {
|
||||
return descriptor.getLanguageName();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public String getType() {
|
||||
return descriptor.getType();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public MemoryFile fetch() throws Exception {
|
||||
setState(StateValue.STARTED);
|
||||
|
||||
|
@ -715,30 +715,30 @@ class SubtitleAutoMatchDialog extends JDialog {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public Exception getError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public StateValue getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void setState(StateValue state) {
|
||||
this.state = state;
|
||||
firePropertyChange("state", null, this.state);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getText();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static class QueryTask extends SwingWorker<Collection<File>, Map<File, List<SubtitleDescriptorBean>>> {
|
||||
|
||||
private final Component parent;
|
||||
|
@ -747,7 +747,7 @@ class SubtitleAutoMatchDialog extends JDialog {
|
|||
private final Collection<File> remainingVideos;
|
||||
private final String languageName;
|
||||
|
||||
|
||||
|
||||
public QueryTask(Collection<SubtitleServiceBean> services, Collection<File> videoFiles, String languageName, Component parent) {
|
||||
this.parent = parent;
|
||||
this.services = services;
|
||||
|
@ -755,7 +755,7 @@ class SubtitleAutoMatchDialog extends JDialog {
|
|||
this.languageName = languageName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected Collection<File> doInBackground() throws Exception {
|
||||
for (SubtitleServiceBean service : services) {
|
||||
|
@ -804,24 +804,24 @@ class SubtitleAutoMatchDialog extends JDialog {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static class DownloadTask extends SwingWorker<File, Void> {
|
||||
|
||||
private final File video;
|
||||
private final SubtitleDescriptorBean descriptor;
|
||||
|
||||
|
||||
|
||||
public DownloadTask(File video, SubtitleDescriptorBean descriptor) {
|
||||
this.video = video;
|
||||
this.descriptor = descriptor;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public SubtitleDescriptorBean getSubtitleBean() {
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public File getDestination(MemoryFile subtitle) {
|
||||
if (descriptor.getType() == null && subtitle == null)
|
||||
return null;
|
||||
|
@ -832,7 +832,7 @@ class SubtitleAutoMatchDialog extends JDialog {
|
|||
return new File(video.getParentFile(), formatSubtitle(base, descriptor.getLanguageName(), ext));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected File doInBackground() {
|
||||
try {
|
||||
|
@ -855,7 +855,7 @@ class SubtitleAutoMatchDialog extends JDialog {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected static abstract class SubtitleServiceBean extends AbstractBean {
|
||||
|
||||
private final String name;
|
||||
|
@ -865,35 +865,35 @@ class SubtitleAutoMatchDialog extends JDialog {
|
|||
private StateValue state = StateValue.PENDING;
|
||||
private Throwable error = null;
|
||||
|
||||
|
||||
|
||||
public SubtitleServiceBean(String name, Icon icon, URI link) {
|
||||
this.name = name;
|
||||
this.icon = icon;
|
||||
this.link = link;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public Icon getIcon() {
|
||||
return icon;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public URI getLink() {
|
||||
return link;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public abstract float getMatchProbabilty(File videoFile, SubtitleDescriptor descriptor);
|
||||
|
||||
|
||||
|
||||
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 {
|
||||
setState(StateValue.STARTED);
|
||||
|
||||
|
@ -910,61 +910,61 @@ class SubtitleAutoMatchDialog extends JDialog {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void setState(StateValue state) {
|
||||
this.state = state;
|
||||
firePropertyChange("state", null, this.state);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public StateValue getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public Throwable getError() {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected static class VideoHashSubtitleServiceBean extends SubtitleServiceBean {
|
||||
|
||||
private VideoHashSubtitleService service;
|
||||
|
||||
|
||||
|
||||
public VideoHashSubtitleServiceBean(VideoHashSubtitleService service) {
|
||||
super(service.getName(), service.getIcon(), service.getLink());
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected Map<File, List<SubtitleDescriptor>> getSubtitleList(Collection<File> files, String languageName, Component parent) throws Exception {
|
||||
return service.getSubtitleList(files.toArray(new File[0]), languageName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public float getMatchProbabilty(File videoFile, SubtitleDescriptor descriptor) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected static class SubtitleProviderBean extends SubtitleServiceBean {
|
||||
|
||||
private SubtitleAutoMatchDialog inputProvider;
|
||||
private SubtitleProvider service;
|
||||
|
||||
|
||||
|
||||
public SubtitleProviderBean(SubtitleProvider service, SubtitleAutoMatchDialog inputProvider) {
|
||||
super(service.getName(), service.getIcon(), service.getLink());
|
||||
this.service = service;
|
||||
this.inputProvider = inputProvider;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
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>>();
|
||||
|
@ -973,7 +973,7 @@ class SubtitleAutoMatchDialog extends JDialog {
|
|||
}
|
||||
|
||||
// auto-detect query and search for subtitles
|
||||
Collection<String> querySet = detectSeriesName(files);
|
||||
Collection<String> querySet = detectSeriesNames(files);
|
||||
List<SubtitleDescriptor> subtitles = findSubtitles(service, querySet, languageName);
|
||||
|
||||
// if auto-detection fails, ask user for input
|
||||
|
@ -1016,7 +1016,7 @@ class SubtitleAutoMatchDialog extends JDialog {
|
|||
return subtitlesByFile;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public float getMatchProbabilty(File videoFile, SubtitleDescriptor descriptor) {
|
||||
return EpisodeMetrics.verificationMetric().getSimilarity(videoFile, descriptor) * 0.9f;
|
||||
|
|
Loading…
Reference in New Issue