+ Subtitle Upload Support !!!
This commit is contained in:
parent
fae437f780
commit
2fa1ca6dc2
|
@ -57,6 +57,7 @@ import net.sourceforge.filebot.web.Episode;
|
||||||
import net.sourceforge.filebot.web.Movie;
|
import net.sourceforge.filebot.web.Movie;
|
||||||
import net.sourceforge.filebot.web.MovieIdentificationService;
|
import net.sourceforge.filebot.web.MovieIdentificationService;
|
||||||
import net.sourceforge.filebot.web.SearchResult;
|
import net.sourceforge.filebot.web.SearchResult;
|
||||||
|
import net.sourceforge.filebot.web.TMDbClient.MovieInfo;
|
||||||
import net.sourceforge.filebot.web.TheTVDBClient.SeriesInfo;
|
import net.sourceforge.filebot.web.TheTVDBClient.SeriesInfo;
|
||||||
import net.sourceforge.filebot.web.TheTVDBSearchResult;
|
import net.sourceforge.filebot.web.TheTVDBSearchResult;
|
||||||
|
|
||||||
|
@ -106,6 +107,10 @@ public class MediaDetection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Locale guessLanguageFromSuffix(File file) {
|
||||||
|
return releaseInfo.getLanguageSuffix(getName(file));
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean isEpisode(String name, boolean strict) {
|
public static boolean isEpisode(String name, boolean strict) {
|
||||||
return parseEpisodeNumber(name, strict) != null || parseDate(name) != null;
|
return parseEpisodeNumber(name, strict) != null || parseDate(name) != null;
|
||||||
}
|
}
|
||||||
|
@ -965,6 +970,14 @@ public class MediaDetection {
|
||||||
return WebServices.TheTVDB.getSeriesInfoByID(grepTheTvdbId(new String(readFile(nfo), "UTF-8")).iterator().next(), locale);
|
return WebServices.TheTVDB.getSeriesInfoByID(grepTheTvdbId(new String(readFile(nfo), "UTF-8")).iterator().next(), locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Movie tmdb2imdb(Movie m) throws IOException {
|
||||||
|
if (m.getTmdbId() <= 0 && m.getImdbId() <= 0)
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
|
||||||
|
MovieInfo info = WebServices.TMDb.getMovieInfo(m, Locale.ENGLISH);
|
||||||
|
return new Movie(info.getName(), info.getReleased().getYear(), info.getImdbId(), info.getId());
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Heavy-duty name matcher used for matching a file to or more movies (out of a list of ~50k)
|
* Heavy-duty name matcher used for matching a file to or more movies (out of a list of ~50k)
|
||||||
*/
|
*/
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 666 B After Width: | Height: | Size: 523 B |
Binary file not shown.
Before Width: | Height: | Size: 630 B After Width: | Height: | Size: 684 B |
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
|
@ -1,7 +1,5 @@
|
||||||
|
|
||||||
package net.sourceforge.filebot.ui;
|
package net.sourceforge.filebot.ui;
|
||||||
|
|
||||||
|
|
||||||
import static javax.swing.ScrollPaneConstants.*;
|
import static javax.swing.ScrollPaneConstants.*;
|
||||||
import static net.sourceforge.filebot.ui.NotificationLogging.*;
|
import static net.sourceforge.filebot.ui.NotificationLogging.*;
|
||||||
import static net.sourceforge.tuned.ui.TunedUtilities.*;
|
import static net.sourceforge.tuned.ui.TunedUtilities.*;
|
||||||
|
@ -44,7 +42,6 @@ import ca.odell.glazedlists.EventList;
|
||||||
import ca.odell.glazedlists.matchers.TextMatcherEditor;
|
import ca.odell.glazedlists.matchers.TextMatcherEditor;
|
||||||
import ca.odell.glazedlists.swing.AutoCompleteSupport;
|
import ca.odell.glazedlists.swing.AutoCompleteSupport;
|
||||||
|
|
||||||
|
|
||||||
public abstract class AbstractSearchPanel<S, E> extends JComponent {
|
public abstract class AbstractSearchPanel<S, E> extends JComponent {
|
||||||
|
|
||||||
protected final JPanel tabbedPaneGroup = new JPanel(new MigLayout("nogrid, fill, insets 0", "align center", "[fill]8px[pref!]4px"));
|
protected final JPanel tabbedPaneGroup = new JPanel(new MigLayout("nogrid, fill, insets 0", "align center", "[fill]8px[pref!]4px"));
|
||||||
|
@ -57,7 +54,6 @@ public abstract class AbstractSearchPanel<S, E> extends JComponent {
|
||||||
|
|
||||||
protected final EventList<String> searchHistory = createSearchHistory();
|
protected final EventList<String> searchHistory = createSearchHistory();
|
||||||
|
|
||||||
|
|
||||||
public AbstractSearchPanel() {
|
public AbstractSearchPanel() {
|
||||||
historyPanel.setColumnHeader(2, "Duration");
|
historyPanel.setColumnHeader(2, "Duration");
|
||||||
|
|
||||||
|
@ -68,10 +64,10 @@ public abstract class AbstractSearchPanel<S, E> extends JComponent {
|
||||||
|
|
||||||
tabbedPaneGroup.setBorder(BorderFactory.createTitledBorder("Search Results"));
|
tabbedPaneGroup.setBorder(BorderFactory.createTitledBorder("Search Results"));
|
||||||
tabbedPaneGroup.add(tabbedPane, "grow, wrap");
|
tabbedPaneGroup.add(tabbedPane, "grow, wrap");
|
||||||
setLayout(new MigLayout("nogrid, fill, insets 10px 10px 15px 10px", "align center", "[pref!]10px[fill]"));
|
setLayout(new MigLayout("nogrid, fill, insets 10px 10px 15px 10px", "align 45%", "[pref!]10px[fill]"));
|
||||||
|
|
||||||
add(searchTextField);
|
add(searchTextField);
|
||||||
add(new JButton(searchAction), "gap 18px, id search");
|
add(new JButton(searchAction), "gap 16px, id search, sgy button");
|
||||||
add(tabbedPaneGroup, "newline, grow");
|
add(tabbedPaneGroup, "newline, grow");
|
||||||
|
|
||||||
searchTextField.getEditor().setAction(searchAction);
|
searchTextField.getEditor().setAction(searchAction);
|
||||||
|
@ -99,19 +95,14 @@ public abstract class AbstractSearchPanel<S, E> extends JComponent {
|
||||||
installAction(this, KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), searchAction);
|
installAction(this, KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), searchAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected abstract S[] getSearchEngines();
|
protected abstract S[] getSearchEngines();
|
||||||
|
|
||||||
|
|
||||||
protected abstract LabelProvider<S> getSearchEngineLabelProvider();
|
protected abstract LabelProvider<S> getSearchEngineLabelProvider();
|
||||||
|
|
||||||
|
|
||||||
protected abstract Settings getSettings();
|
protected abstract Settings getSettings();
|
||||||
|
|
||||||
|
|
||||||
protected abstract RequestProcessor<?, E> createRequestProcessor();
|
protected abstract RequestProcessor<?, E> createRequestProcessor();
|
||||||
|
|
||||||
|
|
||||||
private void search(RequestProcessor<?, E> requestProcessor) {
|
private void search(RequestProcessor<?, E> requestProcessor) {
|
||||||
FileBotTab<?> tab = requestProcessor.tab;
|
FileBotTab<?> tab = requestProcessor.tab;
|
||||||
|
|
||||||
|
@ -127,7 +118,6 @@ public abstract class AbstractSearchPanel<S, E> extends JComponent {
|
||||||
new SearchTask(requestProcessor).execute();
|
new SearchTask(requestProcessor).execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected EventList<String> createSearchHistory() {
|
protected EventList<String> createSearchHistory() {
|
||||||
// create in-memory history
|
// create in-memory history
|
||||||
BasicEventList<String> history = new BasicEventList<String>();
|
BasicEventList<String> history = new BasicEventList<String>();
|
||||||
|
@ -158,17 +148,14 @@ public abstract class AbstractSearchPanel<S, E> extends JComponent {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
private class SearchTask extends SwingWorker<Collection<? extends SearchResult>, Void> {
|
private class SearchTask extends SwingWorker<Collection<? extends SearchResult>, Void> {
|
||||||
|
|
||||||
private final RequestProcessor<?, E> requestProcessor;
|
private final RequestProcessor<?, E> requestProcessor;
|
||||||
|
|
||||||
|
|
||||||
public SearchTask(RequestProcessor<?, E> requestProcessor) {
|
public SearchTask(RequestProcessor<?, E> requestProcessor) {
|
||||||
this.requestProcessor = requestProcessor;
|
this.requestProcessor = requestProcessor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Collection<? extends SearchResult> doInBackground() throws Exception {
|
protected Collection<? extends SearchResult> doInBackground() throws Exception {
|
||||||
long start = System.currentTimeMillis();
|
long start = System.currentTimeMillis();
|
||||||
|
@ -180,7 +167,6 @@ public abstract class AbstractSearchPanel<S, E> extends JComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void done() {
|
public void done() {
|
||||||
FileBotTab<?> tab = requestProcessor.tab;
|
FileBotTab<?> tab = requestProcessor.tab;
|
||||||
|
@ -232,17 +218,14 @@ public abstract class AbstractSearchPanel<S, E> extends JComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private class FetchTask extends SwingWorker<Collection<E>, Void> {
|
private class FetchTask extends SwingWorker<Collection<E>, Void> {
|
||||||
|
|
||||||
private final RequestProcessor<?, E> requestProcessor;
|
private final RequestProcessor<?, E> requestProcessor;
|
||||||
|
|
||||||
|
|
||||||
public FetchTask(RequestProcessor<?, E> requestProcessor) {
|
public FetchTask(RequestProcessor<?, E> requestProcessor) {
|
||||||
this.requestProcessor = requestProcessor;
|
this.requestProcessor = requestProcessor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected final Collection<E> doInBackground() throws Exception {
|
protected final Collection<E> doInBackground() throws Exception {
|
||||||
long start = System.currentTimeMillis();
|
long start = System.currentTimeMillis();
|
||||||
|
@ -254,7 +237,6 @@ public abstract class AbstractSearchPanel<S, E> extends JComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void done() {
|
public void done() {
|
||||||
FileBotTab<?> tab = requestProcessor.tab;
|
FileBotTab<?> tab = requestProcessor.tab;
|
||||||
|
@ -288,24 +270,20 @@ public abstract class AbstractSearchPanel<S, E> extends JComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected static class Request {
|
protected static class Request {
|
||||||
|
|
||||||
private final String searchText;
|
private final String searchText;
|
||||||
|
|
||||||
|
|
||||||
public Request(String searchText) {
|
public Request(String searchText) {
|
||||||
this.searchText = searchText;
|
this.searchText = searchText;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public String getSearchText() {
|
public String getSearchText() {
|
||||||
return searchText;
|
return searchText;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected abstract static class RequestProcessor<R extends Request, E> {
|
protected abstract static class RequestProcessor<R extends Request, E> {
|
||||||
|
|
||||||
protected final R request;
|
protected final R request;
|
||||||
|
@ -316,45 +294,35 @@ public abstract class AbstractSearchPanel<S, E> extends JComponent {
|
||||||
|
|
||||||
private long duration = 0;
|
private long duration = 0;
|
||||||
|
|
||||||
|
|
||||||
public RequestProcessor(R request, JComponent component) {
|
public RequestProcessor(R request, JComponent component) {
|
||||||
this.request = request;
|
this.request = request;
|
||||||
this.tab = new FileBotTab<JComponent>(component);
|
this.tab = new FileBotTab<JComponent>(component);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public abstract Collection<? extends SearchResult> search() throws Exception;
|
public abstract Collection<? extends SearchResult> search() throws Exception;
|
||||||
|
|
||||||
|
|
||||||
public abstract Collection<E> fetch() throws Exception;
|
public abstract Collection<E> fetch() throws Exception;
|
||||||
|
|
||||||
|
|
||||||
public abstract void process(Collection<E> elements);
|
public abstract void process(Collection<E> elements);
|
||||||
|
|
||||||
|
|
||||||
public abstract URI getLink();
|
public abstract URI getLink();
|
||||||
|
|
||||||
|
|
||||||
public JComponent getComponent() {
|
public JComponent getComponent() {
|
||||||
return tab.getComponent();
|
return tab.getComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public SearchResult getSearchResult() {
|
public SearchResult getSearchResult() {
|
||||||
return searchResult;
|
return searchResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setSearchResult(SearchResult searchResult) {
|
public void setSearchResult(SearchResult searchResult) {
|
||||||
this.searchResult = searchResult;
|
this.searchResult = searchResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public String getStatusMessage(Collection<E> result) {
|
public String getStatusMessage(Collection<E> result) {
|
||||||
return String.format("%d elements found", result.size());
|
return String.format("%d elements found", result.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public String getTitle() {
|
public String getTitle() {
|
||||||
if (searchResult != null)
|
if (searchResult != null)
|
||||||
return searchResult.getName();
|
return searchResult.getName();
|
||||||
|
@ -362,7 +330,6 @@ public abstract class AbstractSearchPanel<S, E> extends JComponent {
|
||||||
return request.getSearchText();
|
return request.getSearchText();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public String getHistoryEntry() {
|
public String getHistoryEntry() {
|
||||||
SeriesNameMatcher nameMatcher = new SeriesNameMatcher();
|
SeriesNameMatcher nameMatcher = new SeriesNameMatcher();
|
||||||
|
|
||||||
|
@ -371,12 +338,10 @@ public abstract class AbstractSearchPanel<S, E> extends JComponent {
|
||||||
return nameMatcher.matchByFirstCommonWordSequence(searchResult.getName(), request.getSearchText());
|
return nameMatcher.matchByFirstCommonWordSequence(searchResult.getName(), request.getSearchText());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Icon getIcon() {
|
public Icon getIcon() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected SearchResult selectSearchResult(Collection<? extends SearchResult> searchResults, Window window) throws Exception {
|
protected SearchResult selectSearchResult(Collection<? extends SearchResult> searchResults, Window window) throws Exception {
|
||||||
// multiple results have been found, user must select one
|
// multiple results have been found, user must select one
|
||||||
SelectDialog<SearchResult> selectDialog = new SelectDialog<SearchResult>(window, searchResults);
|
SelectDialog<SearchResult> selectDialog = new SelectDialog<SearchResult>(window, searchResults);
|
||||||
|
@ -388,14 +353,12 @@ public abstract class AbstractSearchPanel<S, E> extends JComponent {
|
||||||
return selectDialog.getSelectedValue();
|
return selectDialog.getSelectedValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected void configureSelectDialog(SelectDialog<SearchResult> selectDialog) {
|
protected void configureSelectDialog(SelectDialog<SearchResult> selectDialog) {
|
||||||
selectDialog.setLocation(getOffsetLocation(selectDialog.getOwner()));
|
selectDialog.setLocation(getOffsetLocation(selectDialog.getOwner()));
|
||||||
selectDialog.setIconImage(getImage(getIcon()));
|
selectDialog.setIconImage(getImage(getIcon()));
|
||||||
selectDialog.setMinimumSize(new Dimension(250, 150));
|
selectDialog.setMinimumSize(new Dimension(250, 150));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public long getDuration() {
|
public long getDuration() {
|
||||||
return duration;
|
return duration;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
|
|
||||||
package net.sourceforge.filebot.ui;
|
package net.sourceforge.filebot.ui;
|
||||||
|
|
||||||
|
|
||||||
import static java.util.Arrays.*;
|
import static java.util.Arrays.*;
|
||||||
import static java.util.Collections.*;
|
import static java.util.Collections.*;
|
||||||
|
|
||||||
|
@ -12,46 +10,38 @@ import java.util.Locale;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
|
||||||
public class Language {
|
public class Language {
|
||||||
|
|
||||||
private final String code;
|
private final String code;
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
||||||
|
|
||||||
public Language(String code, String name) {
|
public Language(String code, String name) {
|
||||||
this.code = code;
|
this.code = code;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public String getCode() {
|
public String getCode() {
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Locale toLocale() {
|
public Locale toLocale() {
|
||||||
return new Locale(getCode());
|
return new Locale(getCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Language clone() {
|
public Language clone() {
|
||||||
return new Language(code, name);
|
return new Language(code, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static final Comparator<Language> ALPHABETIC_ORDER = new Comparator<Language>() {
|
public static final Comparator<Language> ALPHABETIC_ORDER = new Comparator<Language>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -60,7 +50,6 @@ public class Language {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
public static Language getLanguage(String code) {
|
public static Language getLanguage(String code) {
|
||||||
ResourceBundle bundle = ResourceBundle.getBundle(Language.class.getName());
|
ResourceBundle bundle = ResourceBundle.getBundle(Language.class.getName());
|
||||||
|
|
||||||
|
@ -74,7 +63,6 @@ public class Language {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static List<Language> getLanguages(String... codes) {
|
public static List<Language> getLanguages(String... codes) {
|
||||||
Language[] languages = new Language[codes.length];
|
Language[] languages = new Language[codes.length];
|
||||||
|
|
||||||
|
@ -85,6 +73,9 @@ public class Language {
|
||||||
return asList(languages);
|
return asList(languages);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Language getLanguage(Locale locale) {
|
||||||
|
return locale == null ? null : getLanguageByName(locale.getDisplayLanguage(Locale.ENGLISH));
|
||||||
|
}
|
||||||
|
|
||||||
public static Language getLanguageByName(String name) {
|
public static Language getLanguageByName(String name) {
|
||||||
for (Language it : availableLanguages()) {
|
for (Language it : availableLanguages()) {
|
||||||
|
@ -95,7 +86,6 @@ public class Language {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static String getISO3LanguageCodeByName(String languageName) {
|
public static String getISO3LanguageCodeByName(String languageName) {
|
||||||
Language language = Language.getLanguageByName(languageName);
|
Language language = Language.getLanguageByName(languageName);
|
||||||
if (language != null) {
|
if (language != null) {
|
||||||
|
@ -109,19 +99,16 @@ public class Language {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static List<Language> availableLanguages() {
|
public static List<Language> availableLanguages() {
|
||||||
ResourceBundle bundle = ResourceBundle.getBundle(Language.class.getName());
|
ResourceBundle bundle = ResourceBundle.getBundle(Language.class.getName());
|
||||||
return getLanguages(bundle.getString("languages.all").split(","));
|
return getLanguages(bundle.getString("languages.all").split(","));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static List<Language> commonLanguages() {
|
public static List<Language> commonLanguages() {
|
||||||
ResourceBundle bundle = ResourceBundle.getBundle(Language.class.getName());
|
ResourceBundle bundle = ResourceBundle.getBundle(Language.class.getName());
|
||||||
return getLanguages(bundle.getString("languages.common").split(","));
|
return getLanguages(bundle.getString("languages.common").split(","));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static List<Language> preferredLanguages() {
|
public static List<Language> preferredLanguages() {
|
||||||
Set<String> codes = new LinkedHashSet<String>();
|
Set<String> codes = new LinkedHashSet<String>();
|
||||||
|
|
||||||
|
|
|
@ -1,38 +1,39 @@
|
||||||
|
|
||||||
package net.sourceforge.filebot.ui;
|
package net.sourceforge.filebot.ui;
|
||||||
|
|
||||||
|
|
||||||
import static java.awt.event.ItemEvent.*;
|
import static java.awt.event.ItemEvent.*;
|
||||||
import static net.sourceforge.filebot.ui.Language.*;
|
import static net.sourceforge.filebot.ui.Language.*;
|
||||||
import static net.sourceforge.filebot.ui.LanguageComboBoxModel.*;
|
|
||||||
|
|
||||||
import java.awt.event.ItemEvent;
|
import java.awt.event.ItemEvent;
|
||||||
import java.awt.event.ItemListener;
|
import java.awt.event.ItemListener;
|
||||||
import java.util.AbstractList;
|
import java.util.AbstractList;
|
||||||
|
import java.util.AbstractMap.SimpleEntry;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
import javax.swing.JComboBox;
|
import javax.swing.JComboBox;
|
||||||
import javax.swing.JComponent;
|
|
||||||
import javax.swing.event.PopupMenuEvent;
|
import javax.swing.event.PopupMenuEvent;
|
||||||
import javax.swing.event.PopupMenuListener;
|
import javax.swing.event.PopupMenuListener;
|
||||||
|
|
||||||
import net.sourceforge.filebot.Settings;
|
import net.sourceforge.filebot.Settings;
|
||||||
import net.sourceforge.tuned.PreferencesList;
|
|
||||||
import net.sourceforge.tuned.PreferencesMap.PreferencesEntry;
|
|
||||||
|
|
||||||
|
|
||||||
public class LanguageComboBox extends JComboBox {
|
public class LanguageComboBox extends JComboBox {
|
||||||
|
|
||||||
private final PreferencesEntry<String> persistentSelectedLanguage;
|
private Entry<String, String> persistentSelectedLanguage;
|
||||||
private final PreferencesList<String> persistentFavoriteLanguages;
|
private List<String> persistentFavoriteLanguages;
|
||||||
|
|
||||||
|
public LanguageComboBox(Language initialSelection, Settings settings) {
|
||||||
public LanguageComboBox(JComponent parent, Language initialSelection) {
|
super(new LanguageComboBoxModel(initialSelection, initialSelection));
|
||||||
super(new LanguageComboBoxModel(initialSelection != ALL_LANGUAGES, initialSelection));
|
|
||||||
setRenderer(new LanguageComboBoxCellRenderer(super.getRenderer()));
|
setRenderer(new LanguageComboBoxCellRenderer(super.getRenderer()));
|
||||||
|
|
||||||
persistentSelectedLanguage = Settings.forPackage(parent.getClass()).entry("language.selected");
|
if (settings != null) {
|
||||||
persistentFavoriteLanguages = Settings.forPackage(parent.getClass()).node("language.favorites").asList();
|
persistentSelectedLanguage = settings.entry("language.selected");
|
||||||
|
persistentFavoriteLanguages = settings.node("language.favorites").asList();
|
||||||
|
} else {
|
||||||
|
persistentSelectedLanguage = new SimpleEntry<String, String>(null, null);
|
||||||
|
persistentFavoriteLanguages = new ArrayList<String>();
|
||||||
|
}
|
||||||
|
|
||||||
// restore selected language
|
// restore selected language
|
||||||
getModel().setSelectedItem(Language.getLanguage(persistentSelectedLanguage.getValue()));
|
getModel().setSelectedItem(Language.getLanguage(persistentSelectedLanguage.getValue()));
|
||||||
|
@ -57,14 +58,14 @@ public class LanguageComboBox extends JComboBox {
|
||||||
Language language = (Language) e.getItem();
|
Language language = (Language) e.getItem();
|
||||||
|
|
||||||
if (getModel().favorites().add(language)) {
|
if (getModel().favorites().add(language)) {
|
||||||
persistentFavoriteLanguages.set(new AbstractList<String>() {
|
persistentFavoriteLanguages.clear();
|
||||||
|
persistentFavoriteLanguages.addAll(new AbstractList<String>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String get(int index) {
|
public String get(int index) {
|
||||||
return getModel().favorites().get(index).getCode();
|
return getModel().favorites().get(index).getCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int size() {
|
public int size() {
|
||||||
return getModel().favorites().size();
|
return getModel().favorites().size();
|
||||||
|
@ -77,18 +78,15 @@ public class LanguageComboBox extends JComboBox {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LanguageComboBoxModel getModel() {
|
public LanguageComboBoxModel getModel() {
|
||||||
return (LanguageComboBoxModel) super.getModel();
|
return (LanguageComboBoxModel) super.getModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static class PopupSelectionListener implements PopupMenuListener, ItemListener {
|
private static class PopupSelectionListener implements PopupMenuListener, ItemListener {
|
||||||
|
|
||||||
private Object selected = null;
|
private Object selected = null;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
|
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
|
||||||
JComboBox comboBox = (JComboBox) e.getSource();
|
JComboBox comboBox = (JComboBox) e.getSource();
|
||||||
|
@ -97,7 +95,6 @@ public class LanguageComboBox extends JComboBox {
|
||||||
selected = comboBox.getSelectedItem();
|
selected = comboBox.getSelectedItem();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
|
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
|
||||||
JComboBox comboBox = (JComboBox) e.getSource();
|
JComboBox comboBox = (JComboBox) e.getSource();
|
||||||
|
@ -110,13 +107,11 @@ public class LanguageComboBox extends JComboBox {
|
||||||
selected = null;
|
selected = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void popupMenuCanceled(PopupMenuEvent e) {
|
public void popupMenuCanceled(PopupMenuEvent e) {
|
||||||
selected = null;
|
selected = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void itemStateChanged(ItemEvent e) {
|
public void itemStateChanged(ItemEvent e) {
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
|
|
||||||
package net.sourceforge.filebot.ui;
|
package net.sourceforge.filebot.ui;
|
||||||
|
|
||||||
|
|
||||||
import static net.sourceforge.filebot.ui.Language.*;
|
import static net.sourceforge.filebot.ui.Language.*;
|
||||||
|
|
||||||
import java.util.AbstractList;
|
import java.util.AbstractList;
|
||||||
|
@ -12,35 +10,30 @@ import java.util.Set;
|
||||||
import javax.swing.AbstractListModel;
|
import javax.swing.AbstractListModel;
|
||||||
import javax.swing.ComboBoxModel;
|
import javax.swing.ComboBoxModel;
|
||||||
|
|
||||||
|
|
||||||
public class LanguageComboBoxModel extends AbstractListModel implements ComboBoxModel {
|
public class LanguageComboBoxModel extends AbstractListModel implements ComboBoxModel {
|
||||||
|
|
||||||
public static final Language ALL_LANGUAGES = new Language("undefined", "All Languages");
|
public static final Language ALL_LANGUAGES = new Language("undefined", "All Languages");
|
||||||
|
|
||||||
private boolean requireSpecificLanguage;
|
private Language defaultLanguage;
|
||||||
private Language selection;
|
private Language selection;
|
||||||
|
|
||||||
private List<Language> favorites = new Favorites(2);
|
private List<Language> favorites = new Favorites(2);
|
||||||
|
|
||||||
private List<Language> values = availableLanguages();
|
private List<Language> values = availableLanguages();
|
||||||
|
|
||||||
|
public LanguageComboBoxModel(Language defaultLanguage, Language initialSelection) {
|
||||||
public LanguageComboBoxModel(boolean requireSpecificLanguage, Language initialSelection) {
|
this.defaultLanguage = defaultLanguage;
|
||||||
this.requireSpecificLanguage = requireSpecificLanguage;
|
|
||||||
this.selection = initialSelection;
|
this.selection = initialSelection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Language getElementAt(int index) {
|
public Language getElementAt(int index) {
|
||||||
// "All Languages"
|
// "All Languages"
|
||||||
if (!requireSpecificLanguage) {
|
|
||||||
if (index == 0)
|
if (index == 0)
|
||||||
return ALL_LANGUAGES;
|
return defaultLanguage;
|
||||||
|
|
||||||
// "All Languages" offset
|
// "All Languages" offset
|
||||||
index -= 1;
|
index -= 1;
|
||||||
}
|
|
||||||
|
|
||||||
if (index < favorites.size()) {
|
if (index < favorites.size()) {
|
||||||
return favorites.get(index);
|
return favorites.get(index);
|
||||||
|
@ -52,25 +45,21 @@ public class LanguageComboBoxModel extends AbstractListModel implements ComboBox
|
||||||
return values.get(index);
|
return values.get(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getSize() {
|
public int getSize() {
|
||||||
// "All Languages" : favorites[] : values[]
|
// "All Languages" : favorites[] : values[]
|
||||||
return (requireSpecificLanguage ? 0 : 1) + favorites.size() + values.size();
|
return 1 + favorites.size() + values.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public List<Language> favorites() {
|
public List<Language> favorites() {
|
||||||
return favorites;
|
return favorites;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Language getSelectedItem() {
|
public Language getSelectedItem() {
|
||||||
return selection;
|
return selection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setSelectedItem(Object value) {
|
public void setSelectedItem(Object value) {
|
||||||
if (value instanceof Language) {
|
if (value instanceof Language) {
|
||||||
|
@ -79,58 +68,49 @@ public class LanguageComboBoxModel extends AbstractListModel implements ComboBox
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected int convertFavoriteIndexToModel(int favoriteIndex) {
|
protected int convertFavoriteIndexToModel(int favoriteIndex) {
|
||||||
return (requireSpecificLanguage ? 0 : 1) + favoriteIndex;
|
return 1 + favoriteIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected void fireFavoritesAdded(int from, int to) {
|
protected void fireFavoritesAdded(int from, int to) {
|
||||||
fireIntervalAdded(this, convertFavoriteIndexToModel(from), convertFavoriteIndexToModel(to));
|
fireIntervalAdded(this, convertFavoriteIndexToModel(from), convertFavoriteIndexToModel(to));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected void fireFavoritesRemoved(int from, int to) {
|
protected void fireFavoritesRemoved(int from, int to) {
|
||||||
fireIntervalRemoved(this, convertFavoriteIndexToModel(from), convertFavoriteIndexToModel(to));
|
fireIntervalRemoved(this, convertFavoriteIndexToModel(from), convertFavoriteIndexToModel(to));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private class Favorites extends AbstractList<Language> implements Set<Language> {
|
private class Favorites extends AbstractList<Language> implements Set<Language> {
|
||||||
|
|
||||||
private final List<Language> data;
|
private final List<Language> data;
|
||||||
|
|
||||||
private final int capacity;
|
private final int capacity;
|
||||||
|
|
||||||
|
|
||||||
public Favorites(int capacity) {
|
public Favorites(int capacity) {
|
||||||
this.data = new ArrayList<Language>(capacity);
|
this.data = new ArrayList<Language>(capacity);
|
||||||
this.capacity = capacity;
|
this.capacity = capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Language get(int index) {
|
public Language get(int index) {
|
||||||
return data.get(index);
|
return data.get(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean add(Language element) {
|
public boolean add(Language element) {
|
||||||
// add first
|
// add first
|
||||||
return addIfAbsent(0, element);
|
return addIfAbsent(0, element);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void add(int index, Language element) {
|
public void add(int index, Language element) {
|
||||||
addIfAbsent(index, element);
|
addIfAbsent(index, element);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean addIfAbsent(int index, Language element) {
|
public boolean addIfAbsent(int index, Language element) {
|
||||||
// 1. ignore null values
|
// 1. ignore null values
|
||||||
// 2. ignore ALL_LANGUAGES
|
// 2. ignore ALL_LANGUAGES
|
||||||
// 3. make sure there are no duplicates
|
// 3. make sure there are no duplicates
|
||||||
// 4. limit size to capacity
|
// 4. limit size to capacity
|
||||||
if (element == null || element == ALL_LANGUAGES || contains(element) || index >= capacity) {
|
if (element == null || element == ALL_LANGUAGES || element.getCode().equals(defaultLanguage.getCode()) || contains(element) || index >= capacity) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,7 +130,6 @@ public class LanguageComboBoxModel extends AbstractListModel implements ComboBox
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean contains(Object obj) {
|
public boolean contains(Object obj) {
|
||||||
// check via language code, because data consists of clones
|
// check via language code, because data consists of clones
|
||||||
|
@ -166,7 +145,6 @@ public class LanguageComboBoxModel extends AbstractListModel implements ComboBox
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Language remove(int index) {
|
public Language remove(int index) {
|
||||||
Language old = data.remove(index);
|
Language old = data.remove(index);
|
||||||
|
@ -177,7 +155,6 @@ public class LanguageComboBoxModel extends AbstractListModel implements ComboBox
|
||||||
return old;
|
return old;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int size() {
|
public int size() {
|
||||||
return data.size();
|
return data.size();
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
|
|
||||||
package net.sourceforge.filebot.ui.episodelist;
|
package net.sourceforge.filebot.ui.episodelist;
|
||||||
|
|
||||||
|
|
||||||
import static net.sourceforge.filebot.ui.episodelist.SeasonSpinnerModel.*;
|
import static net.sourceforge.filebot.ui.episodelist.SeasonSpinnerModel.*;
|
||||||
import static net.sourceforge.filebot.web.EpisodeUtilities.*;
|
import static net.sourceforge.filebot.web.EpisodeUtilities.*;
|
||||||
|
|
||||||
|
@ -54,14 +52,12 @@ import net.sourceforge.tuned.ui.SelectButton;
|
||||||
import net.sourceforge.tuned.ui.SimpleLabelProvider;
|
import net.sourceforge.tuned.ui.SimpleLabelProvider;
|
||||||
import net.sourceforge.tuned.ui.TunedUtilities;
|
import net.sourceforge.tuned.ui.TunedUtilities;
|
||||||
|
|
||||||
|
|
||||||
public class EpisodeListPanel extends AbstractSearchPanel<EpisodeListProvider, Episode> {
|
public class EpisodeListPanel extends AbstractSearchPanel<EpisodeListProvider, Episode> {
|
||||||
|
|
||||||
private SeasonSpinnerModel seasonSpinnerModel = new SeasonSpinnerModel();
|
private SeasonSpinnerModel seasonSpinnerModel = new SeasonSpinnerModel();
|
||||||
private LanguageComboBox languageComboBox = new LanguageComboBox(this, Language.getLanguage("en"));
|
private LanguageComboBox languageComboBox = new LanguageComboBox(Language.getLanguage("en"), getSettings());
|
||||||
private JComboBox sortOrderComboBox = new JComboBox(SortOrder.values());
|
private JComboBox sortOrderComboBox = new JComboBox(SortOrder.values());
|
||||||
|
|
||||||
|
|
||||||
public EpisodeListPanel() {
|
public EpisodeListPanel() {
|
||||||
historyPanel.setColumnHeader(0, "Show");
|
historyPanel.setColumnHeader(0, "Show");
|
||||||
historyPanel.setColumnHeader(1, "Number of Episodes");
|
historyPanel.setColumnHeader(1, "Number of Episodes");
|
||||||
|
@ -73,9 +69,9 @@ public class EpisodeListPanel extends AbstractSearchPanel<EpisodeListProvider, E
|
||||||
seasonSpinner.setMinimumSize(seasonSpinner.getPreferredSize());
|
seasonSpinner.setMinimumSize(seasonSpinner.getPreferredSize());
|
||||||
|
|
||||||
// add after text field
|
// add after text field
|
||||||
add(seasonSpinner, "sgy combo, gap indent", 1);
|
add(seasonSpinner, "sgy button, gap indent", 1);
|
||||||
add(sortOrderComboBox, "sgy combo, gap rel", 2);
|
add(sortOrderComboBox, "sgy button, gap rel", 2);
|
||||||
add(languageComboBox, "sgy combo, gap indent+5", 3);
|
add(languageComboBox, "sgy button, gap indent+5", 3);
|
||||||
|
|
||||||
// add after tabbed pane
|
// add after tabbed pane
|
||||||
tabbedPaneGroup.add(new JButton(new SaveAction(new SelectedTabExportHandler())));
|
tabbedPaneGroup.add(new JButton(new SaveAction(new SelectedTabExportHandler())));
|
||||||
|
@ -86,25 +82,21 @@ public class EpisodeListPanel extends AbstractSearchPanel<EpisodeListProvider, E
|
||||||
TunedUtilities.installAction(this, KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, KeyEvent.SHIFT_MASK), new SpinSeasonAction(-1));
|
TunedUtilities.installAction(this, KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, KeyEvent.SHIFT_MASK), new SpinSeasonAction(-1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected EpisodeListProvider[] getSearchEngines() {
|
protected EpisodeListProvider[] getSearchEngines() {
|
||||||
return WebServices.getEpisodeListProviders();
|
return WebServices.getEpisodeListProviders();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected LabelProvider<EpisodeListProvider> getSearchEngineLabelProvider() {
|
protected LabelProvider<EpisodeListProvider> getSearchEngineLabelProvider() {
|
||||||
return SimpleLabelProvider.forClass(EpisodeListProvider.class);
|
return SimpleLabelProvider.forClass(EpisodeListProvider.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Settings getSettings() {
|
protected Settings getSettings() {
|
||||||
return Settings.forPackage(EpisodeListPanel.class);
|
return Settings.forPackage(EpisodeListPanel.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected EpisodeListRequestProcessor createRequestProcessor() {
|
protected EpisodeListRequestProcessor createRequestProcessor() {
|
||||||
EpisodeListProvider provider = searchTextField.getSelectButton().getSelectedValue();
|
EpisodeListProvider provider = searchTextField.getSelectButton().getSelectedValue();
|
||||||
|
@ -131,7 +123,6 @@ public class EpisodeListPanel extends AbstractSearchPanel<EpisodeListProvider, E
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
private class SpinSeasonAction extends AbstractAction {
|
private class SpinSeasonAction extends AbstractAction {
|
||||||
|
|
||||||
public SpinSeasonAction(int spin) {
|
public SpinSeasonAction(int spin) {
|
||||||
|
@ -139,14 +130,12 @@ public class EpisodeListPanel extends AbstractSearchPanel<EpisodeListProvider, E
|
||||||
putValue("spin", spin);
|
putValue("spin", spin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
seasonSpinnerModel.spin((Integer) getValue("spin"));
|
seasonSpinnerModel.spin((Integer) getValue("spin"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private class SelectedTabExportHandler implements FileExportHandler {
|
private class SelectedTabExportHandler implements FileExportHandler {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -163,7 +152,6 @@ public class EpisodeListPanel extends AbstractSearchPanel<EpisodeListProvider, E
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canExport() {
|
public boolean canExport() {
|
||||||
FileExportHandler handler = getExportHandler();
|
FileExportHandler handler = getExportHandler();
|
||||||
|
@ -174,13 +162,11 @@ public class EpisodeListPanel extends AbstractSearchPanel<EpisodeListProvider, E
|
||||||
return handler.canExport();
|
return handler.canExport();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void export(File file) throws IOException {
|
public void export(File file) throws IOException {
|
||||||
getExportHandler().export(file);
|
getExportHandler().export(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getDefaultFileName() {
|
public String getDefaultFileName() {
|
||||||
return getExportHandler().getDefaultFileName();
|
return getExportHandler().getDefaultFileName();
|
||||||
|
@ -188,7 +174,6 @@ public class EpisodeListPanel extends AbstractSearchPanel<EpisodeListProvider, E
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected static class EpisodeListRequest extends Request {
|
protected static class EpisodeListRequest extends Request {
|
||||||
|
|
||||||
public final EpisodeListProvider provider;
|
public final EpisodeListProvider provider;
|
||||||
|
@ -196,7 +181,6 @@ public class EpisodeListPanel extends AbstractSearchPanel<EpisodeListProvider, E
|
||||||
public final SortOrder order;
|
public final SortOrder order;
|
||||||
public final Locale language;
|
public final Locale language;
|
||||||
|
|
||||||
|
|
||||||
public EpisodeListRequest(EpisodeListProvider provider, String searchText, int season, SortOrder order, Locale language) {
|
public EpisodeListRequest(EpisodeListProvider provider, String searchText, int season, SortOrder order, Locale language) {
|
||||||
super(searchText);
|
super(searchText);
|
||||||
this.provider = provider;
|
this.provider = provider;
|
||||||
|
@ -206,20 +190,17 @@ public class EpisodeListPanel extends AbstractSearchPanel<EpisodeListProvider, E
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected static class EpisodeListRequestProcessor extends RequestProcessor<EpisodeListRequest, Episode> {
|
protected static class EpisodeListRequestProcessor extends RequestProcessor<EpisodeListRequest, Episode> {
|
||||||
|
|
||||||
public EpisodeListRequestProcessor(EpisodeListRequest request) {
|
public EpisodeListRequestProcessor(EpisodeListRequest request) {
|
||||||
super(request, new EpisodeListTab());
|
super(request, new EpisodeListTab());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<SearchResult> search() throws Exception {
|
public Collection<SearchResult> search() throws Exception {
|
||||||
return request.provider.search(request.getSearchText(), request.language);
|
return request.provider.search(request.getSearchText(), request.language);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<Episode> fetch() throws Exception {
|
public Collection<Episode> fetch() throws Exception {
|
||||||
List<Episode> episodes = request.provider.getEpisodeList(getSearchResult(), request.order, request.language);
|
List<Episode> episodes = request.provider.getEpisodeList(getSearchResult(), request.order, request.language);
|
||||||
|
@ -236,13 +217,11 @@ public class EpisodeListPanel extends AbstractSearchPanel<EpisodeListProvider, E
|
||||||
return episodes;
|
return episodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public URI getLink() {
|
public URI getLink() {
|
||||||
return request.provider.getEpisodeListLink(getSearchResult());
|
return request.provider.getEpisodeListLink(getSearchResult());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void process(Collection<Episode> episodes) {
|
public void process(Collection<Episode> episodes) {
|
||||||
// set a proper title for the export handler before adding episodes
|
// set a proper title for the export handler before adding episodes
|
||||||
|
@ -251,19 +230,16 @@ public class EpisodeListPanel extends AbstractSearchPanel<EpisodeListProvider, E
|
||||||
getComponent().getModel().addAll(episodes);
|
getComponent().getModel().addAll(episodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getStatusMessage(Collection<Episode> result) {
|
public String getStatusMessage(Collection<Episode> result) {
|
||||||
return (result.isEmpty()) ? "No episodes found" : String.format("%d episodes", result.size());
|
return (result.isEmpty()) ? "No episodes found" : String.format("%d episodes", result.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public EpisodeListTab getComponent() {
|
public EpisodeListTab getComponent() {
|
||||||
return (EpisodeListTab) super.getComponent();
|
return (EpisodeListTab) super.getComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getTitle() {
|
public String getTitle() {
|
||||||
if (request.season == ALL_SEASONS)
|
if (request.season == ALL_SEASONS)
|
||||||
|
@ -273,13 +249,11 @@ public class EpisodeListPanel extends AbstractSearchPanel<EpisodeListProvider, E
|
||||||
return String.format("%s - Season %d", super.getTitle(), request.season);
|
return String.format("%s - Season %d", super.getTitle(), request.season);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Icon getIcon() {
|
public Icon getIcon() {
|
||||||
return request.provider.getIcon();
|
return request.provider.getIcon();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void configureSelectDialog(SelectDialog<SearchResult> selectDialog) {
|
protected void configureSelectDialog(SelectDialog<SearchResult> selectDialog) {
|
||||||
super.configureSelectDialog(selectDialog);
|
super.configureSelectDialog(selectDialog);
|
||||||
|
@ -288,7 +262,6 @@ public class EpisodeListPanel extends AbstractSearchPanel<EpisodeListProvider, E
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected static class EpisodeListTab extends FileBotList<Episode> {
|
protected static class EpisodeListTab extends FileBotList<Episode> {
|
||||||
|
|
||||||
public EpisodeListTab() {
|
public EpisodeListTab() {
|
||||||
|
@ -306,14 +279,12 @@ public class EpisodeListPanel extends AbstractSearchPanel<EpisodeListProvider, E
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected static class EpisodeListExportHandler extends FileBotListExportHandler implements ClipboardHandler {
|
protected static class EpisodeListExportHandler extends FileBotListExportHandler implements ClipboardHandler {
|
||||||
|
|
||||||
public EpisodeListExportHandler(FileBotList<Episode> list) {
|
public EpisodeListExportHandler(FileBotList<Episode> list) {
|
||||||
super(list);
|
super(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Transferable createTransferable(JComponent c) {
|
public Transferable createTransferable(JComponent c) {
|
||||||
Transferable episodeArray = new ArrayTransferable<Episode>(list.getModel().toArray(new Episode[0]));
|
Transferable episodeArray = new ArrayTransferable<Episode>(list.getModel().toArray(new Episode[0]));
|
||||||
|
@ -322,7 +293,6 @@ public class EpisodeListPanel extends AbstractSearchPanel<EpisodeListProvider, E
|
||||||
return new CompositeTranserable(episodeArray, textFile);
|
return new CompositeTranserable(episodeArray, textFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void exportToClipboard(JComponent c, Clipboard clipboard, int action) throws IllegalStateException {
|
public void exportToClipboard(JComponent c, Clipboard clipboard, int action) throws IllegalStateException {
|
||||||
Object[] selection = list.getListComponent().getSelectedValues();
|
Object[] selection = list.getListComponent().getSelectedValues();
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
|
|
||||||
package net.sourceforge.filebot.ui.subtitle;
|
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.media.MediaDetection.*;
|
import static net.sourceforge.filebot.media.MediaDetection.*;
|
||||||
|
@ -74,7 +72,6 @@ import net.sourceforge.tuned.ui.EmptySelectionModel;
|
||||||
import net.sourceforge.tuned.ui.LinkButton;
|
import net.sourceforge.tuned.ui.LinkButton;
|
||||||
import net.sourceforge.tuned.ui.RoundBorder;
|
import net.sourceforge.tuned.ui.RoundBorder;
|
||||||
|
|
||||||
|
|
||||||
class SubtitleAutoMatchDialog extends JDialog {
|
class SubtitleAutoMatchDialog extends JDialog {
|
||||||
|
|
||||||
private static final Color hashMatchColor = new Color(0xFAFAD2); // LightGoldenRodYellow
|
private static final Color hashMatchColor = new Color(0xFAFAD2); // LightGoldenRodYellow
|
||||||
|
@ -88,7 +85,6 @@ 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);
|
||||||
|
|
||||||
|
@ -103,7 +99,6 @@ 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());
|
||||||
|
@ -113,7 +108,6 @@ 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());
|
||||||
|
@ -149,22 +143,18 @@ 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);
|
||||||
|
@ -193,7 +183,6 @@ class SubtitleAutoMatchDialog extends JDialog {
|
||||||
// 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 +192,6 @@ 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 +220,6 @@ 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()) {
|
||||||
|
|
||||||
|
@ -268,7 +255,7 @@ class SubtitleAutoMatchDialog extends JDialog {
|
||||||
public void actionPerformed(ActionEvent evt) {
|
public void actionPerformed(ActionEvent evt) {
|
||||||
// disable any active cell editor
|
// disable any active cell editor
|
||||||
if (subtitleMappingTable.getCellEditor() != null) {
|
if (subtitleMappingTable.getCellEditor() != null) {
|
||||||
subtitleMappingTable.getCellEditor().cancelCellEditing();
|
subtitleMappingTable.getCellEditor().stopCellEditing();
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't allow restart of download as long as there are still unfinished download tasks
|
// don't allow restart of download as long as there are still unfinished download tasks
|
||||||
|
@ -360,17 +347,14 @@ 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;
|
||||||
|
@ -412,12 +396,10 @@ 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);
|
||||||
|
@ -443,14 +425,12 @@ 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];
|
||||||
|
|
||||||
|
@ -460,7 +440,6 @@ class SubtitleAutoMatchDialog extends JDialog {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public List<File> getVideoFiles() {
|
public List<File> getVideoFiles() {
|
||||||
return new AbstractList<File>() {
|
return new AbstractList<File>() {
|
||||||
|
|
||||||
|
@ -469,7 +448,6 @@ 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;
|
||||||
|
@ -477,13 +455,11 @@ 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;
|
||||||
|
@ -494,13 +470,11 @@ 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) {
|
||||||
|
@ -513,13 +487,11 @@ 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) {
|
||||||
|
@ -532,19 +504,16 @@ 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) {
|
||||||
|
@ -557,17 +526,14 @@ 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
|
||||||
|
@ -576,7 +542,6 @@ class SubtitleAutoMatchDialog extends JDialog {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static class SubtitleMapping extends AbstractBean {
|
private static class SubtitleMapping extends AbstractBean {
|
||||||
|
|
||||||
private File videoFile;
|
private File videoFile;
|
||||||
|
@ -585,38 +550,31 @@ 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);
|
||||||
|
@ -628,12 +586,10 @@ 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);
|
||||||
|
|
||||||
|
@ -651,7 +607,6 @@ class SubtitleAutoMatchDialog extends JDialog {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static class SubtitleDescriptorBean extends AbstractBean {
|
private static class SubtitleDescriptorBean extends AbstractBean {
|
||||||
|
|
||||||
private final File videoFile;
|
private final File videoFile;
|
||||||
|
@ -661,39 +616,32 @@ 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);
|
||||||
|
|
||||||
|
@ -713,30 +661,25 @@ 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;
|
||||||
|
@ -745,7 +688,6 @@ 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;
|
||||||
|
@ -753,7 +695,6 @@ 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) {
|
||||||
|
@ -802,24 +743,20 @@ 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;
|
||||||
|
@ -830,7 +767,6 @@ 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 {
|
||||||
|
@ -853,7 +789,6 @@ 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;
|
||||||
|
@ -863,35 +798,28 @@ 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);
|
||||||
|
|
||||||
|
@ -908,61 +836,51 @@ 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 {
|
||||||
// ignore clutter files from processing
|
// ignore clutter files from processing
|
||||||
|
@ -1028,7 +946,6 @@ class SubtitleAutoMatchDialog extends JDialog {
|
||||||
return subtitlesByFile;
|
return subtitlesByFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public float getMatchProbabilty(File videoFile, SubtitleDescriptor descriptor) {
|
public float getMatchProbabilty(File videoFile, SubtitleDescriptor descriptor) {
|
||||||
SimilarityMetric metric = new MetricCascade(EpisodeMetrics.SeasonEpisode, EpisodeMetrics.AirDate, EpisodeMetrics.Name);
|
SimilarityMetric metric = new MetricCascade(EpisodeMetrics.SeasonEpisode, EpisodeMetrics.AirDate, EpisodeMetrics.Name);
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
|
|
||||||
package net.sourceforge.filebot.ui.subtitle;
|
package net.sourceforge.filebot.ui.subtitle;
|
||||||
|
|
||||||
|
|
||||||
import static net.sourceforge.filebot.MediaTypes.*;
|
import static net.sourceforge.filebot.MediaTypes.*;
|
||||||
|
import static net.sourceforge.filebot.media.MediaDetection.*;
|
||||||
import static net.sourceforge.filebot.ui.NotificationLogging.*;
|
import static net.sourceforge.filebot.ui.NotificationLogging.*;
|
||||||
import static net.sourceforge.filebot.ui.transfer.FileTransferable.*;
|
import static net.sourceforge.filebot.ui.transfer.FileTransferable.*;
|
||||||
import static net.sourceforge.tuned.FileUtilities.*;
|
import static net.sourceforge.tuned.FileUtilities.*;
|
||||||
|
@ -21,9 +20,12 @@ import java.awt.event.ActionListener;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.TreeSet;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
|
@ -33,19 +35,18 @@ import javax.swing.JFileChooser;
|
||||||
import javax.swing.filechooser.FileNameExtensionFilter;
|
import javax.swing.filechooser.FileNameExtensionFilter;
|
||||||
|
|
||||||
import net.sourceforge.filebot.ResourceManager;
|
import net.sourceforge.filebot.ResourceManager;
|
||||||
|
import net.sourceforge.filebot.web.OpenSubtitlesClient;
|
||||||
import net.sourceforge.filebot.web.SubtitleProvider;
|
import net.sourceforge.filebot.web.SubtitleProvider;
|
||||||
import net.sourceforge.filebot.web.VideoHashSubtitleService;
|
import net.sourceforge.filebot.web.VideoHashSubtitleService;
|
||||||
|
import net.sourceforge.tuned.FileUtilities;
|
||||||
|
import net.sourceforge.tuned.FileUtilities.ParentFilter;
|
||||||
|
|
||||||
abstract class SubtitleDropTarget extends JButton {
|
abstract class SubtitleDropTarget extends JButton {
|
||||||
|
|
||||||
private enum DropAction {
|
private enum DropAction {
|
||||||
Download,
|
Accept, Cancel
|
||||||
Upload,
|
|
||||||
Cancel
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public SubtitleDropTarget() {
|
public SubtitleDropTarget() {
|
||||||
setHorizontalAlignment(CENTER);
|
setHorizontalAlignment(CENTER);
|
||||||
|
|
||||||
|
@ -58,7 +59,7 @@ abstract class SubtitleDropTarget extends JButton {
|
||||||
setOpaque(false);
|
setOpaque(false);
|
||||||
|
|
||||||
// initialize with default mode
|
// initialize with default mode
|
||||||
setDropAction(DropAction.Download);
|
setDropAction(DropAction.Accept);
|
||||||
setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
|
setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
|
||||||
|
|
||||||
// install mouse listener
|
// install mouse listener
|
||||||
|
@ -68,143 +69,21 @@ abstract class SubtitleDropTarget extends JButton {
|
||||||
new DropTarget(this, dropHandler);
|
new DropTarget(this, dropHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void setDropAction(DropAction dropAction) {
|
||||||
private void setDropAction(DropAction dropAction) {
|
|
||||||
setIcon(getIcon(dropAction));
|
setIcon(getIcon(dropAction));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected abstract boolean handleDrop(List<File> files);
|
||||||
|
|
||||||
private Icon getIcon(DropAction dropAction) {
|
protected abstract DropAction getDropAction(List<File> files);
|
||||||
switch (dropAction) {
|
|
||||||
case Download:
|
|
||||||
return ResourceManager.getIcon("subtitle.exact.download");
|
|
||||||
case Upload:
|
|
||||||
return ResourceManager.getIcon("subtitle.exact.upload");
|
|
||||||
default:
|
|
||||||
return ResourceManager.getIcon("message.error");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
protected abstract Icon getIcon(DropAction dropAction);
|
||||||
public abstract VideoHashSubtitleService[] getVideoHashSubtitleServices();
|
|
||||||
|
|
||||||
|
|
||||||
public abstract SubtitleProvider[] getSubtitleProviders();
|
|
||||||
|
|
||||||
|
|
||||||
public abstract String getQueryLanguage();
|
|
||||||
|
|
||||||
|
|
||||||
private boolean handleDownload(List<File> videoFiles) {
|
|
||||||
SubtitleAutoMatchDialog dialog = new SubtitleAutoMatchDialog(getWindow(this));
|
|
||||||
|
|
||||||
// initialize download parameters
|
|
||||||
dialog.setVideoFiles(videoFiles.toArray(new File[0]));
|
|
||||||
|
|
||||||
for (VideoHashSubtitleService service : getVideoHashSubtitleServices()) {
|
|
||||||
dialog.addSubtitleService(service);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (SubtitleProvider service : getSubtitleProviders()) {
|
|
||||||
dialog.addSubtitleService(service);
|
|
||||||
}
|
|
||||||
|
|
||||||
// start looking for subtitles
|
|
||||||
dialog.startQuery(getQueryLanguage());
|
|
||||||
|
|
||||||
// initialize window properties
|
|
||||||
dialog.setIconImage(getImage(getIcon(DropAction.Download)));
|
|
||||||
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
|
|
||||||
dialog.setSize(820, 575);
|
|
||||||
|
|
||||||
// show dialog
|
|
||||||
dialog.setLocation(getOffsetLocation(dialog.getOwner()));
|
|
||||||
dialog.setVisible(true);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private boolean handleUpload(Map<File, File> videosMappedBySubtitle) {
|
|
||||||
// TODO implement upload
|
|
||||||
throw new UnsupportedOperationException("Not implemented yet");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private boolean handleDrop(List<File> files) {
|
|
||||||
// perform a drop action depending on the given files
|
|
||||||
if (containsOnly(files, VIDEO_FILES)) {
|
|
||||||
return handleDownloadLater(files);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (containsOnly(files, FOLDERS)) {
|
|
||||||
// collect all video files from the dropped folders
|
|
||||||
List<File> videoFiles = filter(listFiles(files, 5, false), VIDEO_FILES);
|
|
||||||
|
|
||||||
if (videoFiles.size() > 0) {
|
|
||||||
return handleDownloadLater(videoFiles);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (containsOnly(files, SUBTITLE_FILES)) {
|
|
||||||
// TODO implement upload
|
|
||||||
throw new UnsupportedOperationException("Not implemented yet");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (containsOnlyVideoSubtitleMatches(files)) {
|
|
||||||
// TODO implement upload
|
|
||||||
throw new UnsupportedOperationException("Not implemented yet");
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private boolean handleDownloadLater(final List<File> videoFiles) {
|
|
||||||
// invoke later so we don't block the DnD operation with the download dialog
|
|
||||||
invokeLater(0, new Runnable() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
handleDownload(videoFiles);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private boolean containsOnlyVideoSubtitleMatches(List<File> files) {
|
|
||||||
List<File> subtitles = filter(files, SUBTITLE_FILES);
|
|
||||||
|
|
||||||
if (subtitles.isEmpty())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// number of subtitle files must match the number of video files
|
|
||||||
return subtitles.size() == filter(files, VIDEO_FILES).size();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private DropAction getDropAction(List<File> files) {
|
|
||||||
// video files only, or any folder, containing video files
|
|
||||||
if (containsOnly(files, VIDEO_FILES) || (containsOnly(files, FOLDERS))) {
|
|
||||||
return DropAction.Download;
|
|
||||||
}
|
|
||||||
|
|
||||||
// subtitle files only, or video/subtitle matches
|
|
||||||
if (containsOnly(files, SUBTITLE_FILES) || containsOnlyVideoSubtitleMatches(files)) {
|
|
||||||
return DropAction.Upload;
|
|
||||||
}
|
|
||||||
|
|
||||||
// unknown input
|
|
||||||
return DropAction.Cancel;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final DropTargetAdapter dropHandler = new DropTargetAdapter() {
|
private final DropTargetAdapter dropHandler = new DropTargetAdapter() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dragEnter(DropTargetDragEvent dtde) {
|
public void dragEnter(DropTargetDragEvent dtde) {
|
||||||
DropAction dropAction = DropAction.Download;
|
DropAction dropAction = DropAction.Accept;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
dropAction = getDropAction(getFilesFromTransferable(dtde.getTransferable()));
|
dropAction = getDropAction(getFilesFromTransferable(dtde.getTransferable()));
|
||||||
|
@ -225,14 +104,12 @@ abstract class SubtitleDropTarget extends JButton {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dragExit(DropTargetEvent dte) {
|
public void dragExit(DropTargetEvent dte) {
|
||||||
// reset to default state
|
// reset to default state
|
||||||
setDropAction(DropAction.Download);
|
setDropAction(DropAction.Accept);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void drop(DropTargetDropEvent dtde) {
|
public void drop(DropTargetDropEvent dtde) {
|
||||||
dtde.acceptDrop(DnDConstants.ACTION_REFERENCE);
|
dtde.acceptDrop(DnDConstants.ACTION_REFERENCE);
|
||||||
|
@ -273,4 +150,174 @@ abstract class SubtitleDropTarget extends JButton {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public static abstract class Download extends SubtitleDropTarget {
|
||||||
|
|
||||||
|
public abstract VideoHashSubtitleService[] getVideoHashSubtitleServices();
|
||||||
|
|
||||||
|
public abstract SubtitleProvider[] getSubtitleProviders();
|
||||||
|
|
||||||
|
public abstract String getQueryLanguage();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected DropAction getDropAction(List<File> input) {
|
||||||
|
// accept video files and folders
|
||||||
|
return filter(input, VIDEO_FILES, FOLDERS).size() > 0 ? DropAction.Accept : DropAction.Cancel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean handleDrop(List<File> input) {
|
||||||
|
// perform a drop action depending on the given files
|
||||||
|
final Collection<File> videoFiles = new TreeSet<File>();
|
||||||
|
|
||||||
|
// video files only
|
||||||
|
videoFiles.addAll(filter(input, VIDEO_FILES));
|
||||||
|
videoFiles.addAll(filter(listFiles(filter(input, FOLDERS), 5, false), VIDEO_FILES));
|
||||||
|
|
||||||
|
if (videoFiles.size() > 0) {
|
||||||
|
// invoke later so we don't block the DnD operation with the download dialog
|
||||||
|
invokeLater(0, new Runnable() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
handleDownload(videoFiles);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean handleDownload(Collection<File> videoFiles) {
|
||||||
|
SubtitleAutoMatchDialog dialog = new SubtitleAutoMatchDialog(getWindow(this));
|
||||||
|
|
||||||
|
// initialize download parameters
|
||||||
|
dialog.setVideoFiles(videoFiles.toArray(new File[0]));
|
||||||
|
|
||||||
|
for (VideoHashSubtitleService service : getVideoHashSubtitleServices()) {
|
||||||
|
dialog.addSubtitleService(service);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (SubtitleProvider service : getSubtitleProviders()) {
|
||||||
|
dialog.addSubtitleService(service);
|
||||||
|
}
|
||||||
|
|
||||||
|
// start looking for subtitles
|
||||||
|
dialog.startQuery(getQueryLanguage());
|
||||||
|
|
||||||
|
// initialize window properties
|
||||||
|
dialog.setIconImage(getImage(getIcon(DropAction.Accept)));
|
||||||
|
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
|
||||||
|
dialog.setSize(820, 575);
|
||||||
|
|
||||||
|
// show dialog
|
||||||
|
dialog.setLocation(getOffsetLocation(dialog.getOwner()));
|
||||||
|
dialog.setVisible(true);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Icon getIcon(DropAction dropAction) {
|
||||||
|
switch (dropAction) {
|
||||||
|
case Accept:
|
||||||
|
return ResourceManager.getIcon("subtitle.exact.download");
|
||||||
|
default:
|
||||||
|
return ResourceManager.getIcon("message.error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static abstract class Upload extends SubtitleDropTarget {
|
||||||
|
|
||||||
|
public abstract OpenSubtitlesClient getSubtitleService();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected DropAction getDropAction(List<File> input) {
|
||||||
|
if (getSubtitleService().isAnonymous())
|
||||||
|
return DropAction.Cancel;
|
||||||
|
|
||||||
|
// accept video files and folders
|
||||||
|
return (filter(input, VIDEO_FILES).size() > 0 && filter(input, SUBTITLE_FILES).size() > 0) || filter(input, FOLDERS).size() > 0 ? DropAction.Accept : DropAction.Cancel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean handleDrop(List<File> input) {
|
||||||
|
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
||||||
|
|
||||||
|
// perform a drop action depending on the given files
|
||||||
|
final Collection<File> files = new TreeSet<File>();
|
||||||
|
|
||||||
|
// video files only
|
||||||
|
files.addAll(filter(input, FILES));
|
||||||
|
files.addAll(listFiles(filter(input, FOLDERS), 5, false));
|
||||||
|
|
||||||
|
final List<File> videos = filter(files, VIDEO_FILES);
|
||||||
|
final List<File> subtitles = filter(files, SUBTITLE_FILES);
|
||||||
|
|
||||||
|
final Map<File, File> uploadPlan = new LinkedHashMap<File, File>();
|
||||||
|
for (File subtitle : subtitles) {
|
||||||
|
File video = getVideoForSubtitle(subtitle, filter(videos, new ParentFilter(subtitle.getParentFile())));
|
||||||
|
uploadPlan.put(subtitle, video);
|
||||||
|
}
|
||||||
|
|
||||||
|
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
|
||||||
|
|
||||||
|
if (uploadPlan.size() > 0) {
|
||||||
|
// invoke later so we don't block the DnD operation with the download dialog
|
||||||
|
invokeLater(0, new Runnable() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
handleUpload(uploadPlan);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void handleUpload(Map<File, File> uploadPlan) {
|
||||||
|
SubtitleUploadDialog dialog = new SubtitleUploadDialog(getSubtitleService(), getWindow(this));
|
||||||
|
|
||||||
|
// initialize window properties
|
||||||
|
dialog.setIconImage(getImage(getIcon(DropAction.Accept)));
|
||||||
|
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
|
||||||
|
dialog.setSize(820, 575);
|
||||||
|
|
||||||
|
// show dialog
|
||||||
|
dialog.setLocation(getOffsetLocation(dialog.getOwner()));
|
||||||
|
|
||||||
|
// start processing
|
||||||
|
dialog.setUploadPlan(uploadPlan);
|
||||||
|
dialog.startChecking();
|
||||||
|
|
||||||
|
// show dialog
|
||||||
|
dialog.setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected File getVideoForSubtitle(File subtitle, List<File> videos) {
|
||||||
|
String baseName = stripReleaseInfo(FileUtilities.getName(subtitle)).toLowerCase();
|
||||||
|
|
||||||
|
// find corresponding movie file
|
||||||
|
for (File it : videos) {
|
||||||
|
if (!baseName.isEmpty() && stripReleaseInfo(FileUtilities.getName(it)).toLowerCase().startsWith(baseName)) {
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Icon getIcon(DropAction dropAction) {
|
||||||
|
switch (dropAction) {
|
||||||
|
case Accept:
|
||||||
|
return ResourceManager.getIcon("subtitle.exact.upload");
|
||||||
|
default:
|
||||||
|
return ResourceManager.getIcon("message.error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
|
|
||||||
package net.sourceforge.filebot.ui.subtitle;
|
package net.sourceforge.filebot.ui.subtitle;
|
||||||
|
|
||||||
|
|
||||||
import static net.sourceforge.filebot.Settings.*;
|
import static net.sourceforge.filebot.Settings.*;
|
||||||
import static net.sourceforge.filebot.ui.LanguageComboBoxModel.*;
|
import static net.sourceforge.filebot.ui.LanguageComboBoxModel.*;
|
||||||
import static net.sourceforge.filebot.ui.NotificationLogging.*;
|
import static net.sourceforge.filebot.ui.NotificationLogging.*;
|
||||||
|
@ -49,44 +47,28 @@ import net.sourceforge.filebot.web.VideoHashSubtitleService;
|
||||||
import net.sourceforge.tuned.ui.LabelProvider;
|
import net.sourceforge.tuned.ui.LabelProvider;
|
||||||
import net.sourceforge.tuned.ui.SimpleLabelProvider;
|
import net.sourceforge.tuned.ui.SimpleLabelProvider;
|
||||||
|
|
||||||
|
|
||||||
public class SubtitlePanel extends AbstractSearchPanel<SubtitleProvider, SubtitlePackage> {
|
public class SubtitlePanel extends AbstractSearchPanel<SubtitleProvider, SubtitlePackage> {
|
||||||
|
|
||||||
private LanguageComboBox languageComboBox = new LanguageComboBox(this, ALL_LANGUAGES);
|
private LanguageComboBox languageComboBox = new LanguageComboBox(ALL_LANGUAGES, getSettings());
|
||||||
|
|
||||||
|
|
||||||
public SubtitlePanel() {
|
public SubtitlePanel() {
|
||||||
historyPanel.setColumnHeader(0, "Show / Movie");
|
historyPanel.setColumnHeader(0, "Show / Movie");
|
||||||
historyPanel.setColumnHeader(1, "Number of Subtitles");
|
historyPanel.setColumnHeader(1, "Number of Subtitles");
|
||||||
|
|
||||||
// add after text field
|
// add after text field
|
||||||
add(languageComboBox, "gap indent", 1);
|
add(languageComboBox, "gap indent, sgy button", 1);
|
||||||
add(createImageButton(setUserAction), "width 26px!, height 26px!, gap rel", 2);
|
add(createImageButton(setUserAction), "width 26px!, height 26px!, gap rel, sgy button", 2);
|
||||||
|
|
||||||
// add at the top right corner
|
// add at the top right corner
|
||||||
add(dropTarget, "width 1.6cm!, height 1.2cm!, pos n 0% 100% n", 0);
|
add(uploadDropTarget, "width 1.45cm!, height 1.2cm!, pos n 0% 100%-1.8cm n", 0);
|
||||||
|
add(downloadDropTarget, "width 1.45cm!, height 1.2cm!, pos n 0% 100%-0.15cm n", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final SubtitleDropTarget dropTarget = new SubtitleDropTarget() {
|
private final SubtitleDropTarget uploadDropTarget = new SubtitleDropTarget.Upload() {
|
||||||
|
|
||||||
@Override
|
|
||||||
public VideoHashSubtitleService[] getVideoHashSubtitleServices() {
|
|
||||||
return WebServices.getVideoHashSubtitleServices();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SubtitleProvider[] getSubtitleProviders() {
|
|
||||||
return WebServices.getSubtitleProviders();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getQueryLanguage() {
|
|
||||||
// use currently selected language for drop target
|
|
||||||
return languageComboBox.getModel().getSelectedItem() == ALL_LANGUAGES ? null : languageComboBox.getModel().getSelectedItem().getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
public OpenSubtitlesClient getSubtitleService() {
|
||||||
|
return WebServices.OpenSubtitles;
|
||||||
|
};
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintComponent(Graphics g) {
|
protected void paintComponent(Graphics g) {
|
||||||
|
@ -96,9 +78,10 @@ public class SubtitlePanel extends AbstractSearchPanel<SubtitleProvider, Subtitl
|
||||||
Path2D path = new Path2D.Float();
|
Path2D path = new Path2D.Float();
|
||||||
path.moveTo(0, 0);
|
path.moveTo(0, 0);
|
||||||
path.lineTo(0, getHeight() - 1 - 12);
|
path.lineTo(0, getHeight() - 1 - 12);
|
||||||
path.quadTo(0, getHeight() - 1, 12, getHeight() - 1);
|
path.lineTo(12, getHeight() - 1);
|
||||||
path.lineTo(getWidth(), getHeight() - 1);
|
path.lineTo(getWidth() - 1 - 12, getHeight() - 1);
|
||||||
path.lineTo(getWidth(), 0);
|
path.lineTo(getWidth() - 1, getHeight() - 1 - 12);
|
||||||
|
path.lineTo(getWidth() - 1, 0);
|
||||||
|
|
||||||
g2d.setPaint(getBackground());
|
g2d.setPaint(getBackground());
|
||||||
g2d.fill(path);
|
g2d.fill(path);
|
||||||
|
@ -112,25 +95,64 @@ public class SubtitlePanel extends AbstractSearchPanel<SubtitleProvider, Subtitl
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private final SubtitleDropTarget downloadDropTarget = new SubtitleDropTarget.Download() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VideoHashSubtitleService[] getVideoHashSubtitleServices() {
|
||||||
|
return WebServices.getVideoHashSubtitleServices();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SubtitleProvider[] getSubtitleProviders() {
|
||||||
|
return WebServices.getSubtitleProviders();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getQueryLanguage() {
|
||||||
|
// use currently selected language for drop target
|
||||||
|
return languageComboBox.getModel().getSelectedItem() == ALL_LANGUAGES ? null : languageComboBox.getModel().getSelectedItem().getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void paintComponent(Graphics g) {
|
||||||
|
Graphics2D g2d = (Graphics2D) g.create();
|
||||||
|
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||||
|
|
||||||
|
Path2D path = new Path2D.Float();
|
||||||
|
path.moveTo(0, 0);
|
||||||
|
path.lineTo(0, getHeight() - 1 - 12);
|
||||||
|
path.lineTo(12, getHeight() - 1);
|
||||||
|
path.lineTo(getWidth() - 1 - 12, getHeight() - 1);
|
||||||
|
path.lineTo(getWidth() - 1, getHeight() - 1 - 12);
|
||||||
|
path.lineTo(getWidth() - 1, 0);
|
||||||
|
|
||||||
|
g2d.setPaint(getBackground());
|
||||||
|
g2d.fill(path);
|
||||||
|
|
||||||
|
g2d.setPaint(Color.gray);
|
||||||
|
g2d.draw(path);
|
||||||
|
|
||||||
|
g2d.translate(2, 0);
|
||||||
|
super.paintComponent(g2d);
|
||||||
|
g2d.dispose();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected SubtitleProvider[] getSearchEngines() {
|
protected SubtitleProvider[] getSearchEngines() {
|
||||||
return WebServices.getSubtitleProviders();
|
return WebServices.getSubtitleProviders();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected LabelProvider<SubtitleProvider> getSearchEngineLabelProvider() {
|
protected LabelProvider<SubtitleProvider> getSearchEngineLabelProvider() {
|
||||||
return SimpleLabelProvider.forClass(SubtitleProvider.class);
|
return SimpleLabelProvider.forClass(SubtitleProvider.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Settings getSettings() {
|
protected Settings getSettings() {
|
||||||
return Settings.forPackage(SubtitlePanel.class);
|
return Settings.forPackage(SubtitlePanel.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected SubtitleRequestProcessor createRequestProcessor() {
|
protected SubtitleRequestProcessor createRequestProcessor() {
|
||||||
SubtitleProvider provider = searchTextField.getSelectButton().getSelectedValue();
|
SubtitleProvider provider = searchTextField.getSelectButton().getSelectedValue();
|
||||||
|
@ -140,13 +162,11 @@ public class SubtitlePanel extends AbstractSearchPanel<SubtitleProvider, Subtitl
|
||||||
return new SubtitleRequestProcessor(new SubtitleRequest(provider, text, language));
|
return new SubtitleRequestProcessor(new SubtitleRequest(provider, text, language));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected static class SubtitleRequest extends Request {
|
protected static class SubtitleRequest extends Request {
|
||||||
|
|
||||||
private final SubtitleProvider provider;
|
private final SubtitleProvider provider;
|
||||||
private final Language language;
|
private final Language language;
|
||||||
|
|
||||||
|
|
||||||
public SubtitleRequest(SubtitleProvider provider, String searchText, Language language) {
|
public SubtitleRequest(SubtitleProvider provider, String searchText, Language language) {
|
||||||
super(searchText);
|
super(searchText);
|
||||||
|
|
||||||
|
@ -154,32 +174,27 @@ public class SubtitlePanel extends AbstractSearchPanel<SubtitleProvider, Subtitl
|
||||||
this.language = language;
|
this.language = language;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public SubtitleProvider getProvider() {
|
public SubtitleProvider getProvider() {
|
||||||
return provider;
|
return provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public String getLanguageName() {
|
public String getLanguageName() {
|
||||||
return language == ALL_LANGUAGES ? null : language.getName();
|
return language == ALL_LANGUAGES ? null : language.getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected static class SubtitleRequestProcessor extends RequestProcessor<SubtitleRequest, SubtitlePackage> {
|
protected static class SubtitleRequestProcessor extends RequestProcessor<SubtitleRequest, SubtitlePackage> {
|
||||||
|
|
||||||
public SubtitleRequestProcessor(SubtitleRequest request) {
|
public SubtitleRequestProcessor(SubtitleRequest request) {
|
||||||
super(request, new SubtitleDownloadComponent());
|
super(request, new SubtitleDownloadComponent());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<SearchResult> search() throws Exception {
|
public Collection<SearchResult> search() throws Exception {
|
||||||
return request.getProvider().search(request.getSearchText());
|
return request.getProvider().search(request.getSearchText());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<SubtitlePackage> fetch() throws Exception {
|
public Collection<SubtitlePackage> fetch() throws Exception {
|
||||||
List<SubtitlePackage> packages = new ArrayList<SubtitlePackage>();
|
List<SubtitlePackage> packages = new ArrayList<SubtitlePackage>();
|
||||||
|
@ -191,38 +206,32 @@ public class SubtitlePanel extends AbstractSearchPanel<SubtitleProvider, Subtitl
|
||||||
return packages;
|
return packages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public URI getLink() {
|
public URI getLink() {
|
||||||
return request.getProvider().getSubtitleListLink(getSearchResult(), request.getLanguageName());
|
return request.getProvider().getSubtitleListLink(getSearchResult(), request.getLanguageName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void process(Collection<SubtitlePackage> subtitles) {
|
public void process(Collection<SubtitlePackage> subtitles) {
|
||||||
getComponent().setLanguageVisible(request.getLanguageName() == null);
|
getComponent().setLanguageVisible(request.getLanguageName() == null);
|
||||||
getComponent().getPackageModel().addAll(subtitles);
|
getComponent().getPackageModel().addAll(subtitles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SubtitleDownloadComponent getComponent() {
|
public SubtitleDownloadComponent getComponent() {
|
||||||
return (SubtitleDownloadComponent) super.getComponent();
|
return (SubtitleDownloadComponent) super.getComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getStatusMessage(Collection<SubtitlePackage> result) {
|
public String getStatusMessage(Collection<SubtitlePackage> result) {
|
||||||
return (result.isEmpty()) ? "No subtitles found" : String.format("%d subtitles", result.size());
|
return (result.isEmpty()) ? "No subtitles found" : String.format("%d subtitles", result.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Icon getIcon() {
|
public Icon getIcon() {
|
||||||
return request.provider.getIcon();
|
return request.provider.getIcon();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void configureSelectDialog(SelectDialog<SearchResult> selectDialog) {
|
protected void configureSelectDialog(SelectDialog<SearchResult> selectDialog) {
|
||||||
super.configureSelectDialog(selectDialog);
|
super.configureSelectDialog(selectDialog);
|
||||||
|
|
|
@ -0,0 +1,666 @@
|
||||||
|
package net.sourceforge.filebot.ui.subtitle;
|
||||||
|
|
||||||
|
import static net.sourceforge.filebot.MediaTypes.*;
|
||||||
|
import static net.sourceforge.filebot.media.MediaDetection.*;
|
||||||
|
import static net.sourceforge.tuned.ui.TunedUtilities.*;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.awt.Cursor;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.Window;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.beans.PropertyChangeEvent;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.EventObject;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.swing.AbstractAction;
|
||||||
|
import javax.swing.Action;
|
||||||
|
import javax.swing.DefaultCellEditor;
|
||||||
|
import javax.swing.DefaultListCellRenderer;
|
||||||
|
import javax.swing.Icon;
|
||||||
|
import javax.swing.JButton;
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JDialog;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JList;
|
||||||
|
import javax.swing.JScrollPane;
|
||||||
|
import javax.swing.JTable;
|
||||||
|
import javax.swing.ListCellRenderer;
|
||||||
|
import javax.swing.SwingWorker;
|
||||||
|
import javax.swing.event.CellEditorListener;
|
||||||
|
import javax.swing.table.AbstractTableModel;
|
||||||
|
import javax.swing.table.DefaultTableCellRenderer;
|
||||||
|
import javax.swing.table.TableCellEditor;
|
||||||
|
import javax.swing.table.TableCellRenderer;
|
||||||
|
|
||||||
|
import net.miginfocom.swing.MigLayout;
|
||||||
|
import net.sourceforge.filebot.ResourceManager;
|
||||||
|
import net.sourceforge.filebot.media.MediaDetection;
|
||||||
|
import net.sourceforge.filebot.ui.Language;
|
||||||
|
import net.sourceforge.filebot.ui.LanguageComboBox;
|
||||||
|
import net.sourceforge.filebot.ui.SelectDialog;
|
||||||
|
import net.sourceforge.filebot.web.Movie;
|
||||||
|
import net.sourceforge.filebot.web.OpenSubtitlesClient;
|
||||||
|
import net.sourceforge.filebot.web.VideoHashSubtitleService.CheckResult;
|
||||||
|
import net.sourceforge.tuned.FileUtilities;
|
||||||
|
import net.sourceforge.tuned.ui.AbstractBean;
|
||||||
|
import net.sourceforge.tuned.ui.EmptySelectionModel;
|
||||||
|
|
||||||
|
public class SubtitleUploadDialog extends JDialog {
|
||||||
|
|
||||||
|
private final JTable subtitleMappingTable = createTable();
|
||||||
|
|
||||||
|
private final OpenSubtitlesClient database;
|
||||||
|
|
||||||
|
private ExecutorService checkExecutorService;
|
||||||
|
private ExecutorService uploadExecutorService;
|
||||||
|
|
||||||
|
public SubtitleUploadDialog(OpenSubtitlesClient database, Window owner) {
|
||||||
|
super(owner, "Upload Subtitles", ModalityType.DOCUMENT_MODAL);
|
||||||
|
|
||||||
|
this.database = database;
|
||||||
|
|
||||||
|
JComponent content = (JComponent) getContentPane();
|
||||||
|
content.setLayout(new MigLayout("fill, insets dialog, nogrid", "", "[fill][pref!]"));
|
||||||
|
|
||||||
|
content.add(new JScrollPane(subtitleMappingTable), "grow, wrap");
|
||||||
|
|
||||||
|
content.add(new JButton(uploadAction), "tag ok");
|
||||||
|
content.add(new JButton(finishAction), "tag cancel");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected JTable createTable() {
|
||||||
|
JTable table = new JTable(new SubtitleMappingTableModel());
|
||||||
|
table.setDefaultRenderer(Movie.class, new MovieRenderer());
|
||||||
|
table.setDefaultRenderer(File.class, new FileRenderer());
|
||||||
|
table.setDefaultRenderer(Language.class, new LanguageRenderer());
|
||||||
|
table.setDefaultRenderer(SubtitleMapping.Status.class, new StatusRenderer());
|
||||||
|
|
||||||
|
table.setRowHeight(28);
|
||||||
|
table.setIntercellSpacing(new Dimension(5, 5));
|
||||||
|
|
||||||
|
table.setBackground(Color.white);
|
||||||
|
table.setAutoCreateRowSorter(true);
|
||||||
|
table.setFillsViewportHeight(true);
|
||||||
|
|
||||||
|
LanguageComboBox languageEditor = new LanguageComboBox(Language.getLanguage("en"), null);
|
||||||
|
|
||||||
|
// disable selection
|
||||||
|
table.setSelectionModel(new EmptySelectionModel());
|
||||||
|
languageEditor.setFocusable(false);
|
||||||
|
|
||||||
|
table.setDefaultEditor(Language.class, new DefaultCellEditor(languageEditor) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
|
||||||
|
LanguageComboBox editor = (LanguageComboBox) super.getTableCellEditorComponent(table, value, isSelected, row, column);
|
||||||
|
editor.getModel().setSelectedItem(value);
|
||||||
|
return editor;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
table.setDefaultEditor(Movie.class, new TableCellEditor() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean stopCellEditing() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldSelectCell(EventObject evt) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeCellEditorListener(CellEditorListener listener) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCellEditable(EventObject evt) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getCellEditorValue() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancelCellEditing() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addCellEditorListener(CellEditorListener evt) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
|
||||||
|
table.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
||||||
|
try {
|
||||||
|
SubtitleMappingTableModel model = (SubtitleMappingTableModel) table.getModel();
|
||||||
|
SubtitleMapping mapping = model.getData()[table.convertRowIndexToModel(row)];
|
||||||
|
|
||||||
|
Object originalIdentity = mapping.getIdentity();
|
||||||
|
File video = mapping.getVideo();
|
||||||
|
String input = showInputDialog("Enter movie / series name:", stripReleaseInfo(FileUtilities.getName(video)), String.format("%s/%s", video.getParentFile().getName(), video.getName()), SubtitleUploadDialog.this);
|
||||||
|
if (input != null && input.length() > 0) {
|
||||||
|
List<Movie> options = database.searchMovie(input, Locale.ENGLISH);
|
||||||
|
if (options.size() > 0) {
|
||||||
|
SelectDialog<Movie> dialog = new SelectDialog<Movie>(SubtitleUploadDialog.this, options);
|
||||||
|
dialog.setLocation(getOffsetLocation(dialog.getOwner()));
|
||||||
|
dialog.setVisible(true);
|
||||||
|
Movie selectedValue = dialog.getSelectedValue();
|
||||||
|
if (selectedValue != null) {
|
||||||
|
for (SubtitleMapping it : model.getData()) {
|
||||||
|
if (originalIdentity == it.getIdentity() || (originalIdentity != null && originalIdentity.equals(it.getIdentity()))) {
|
||||||
|
if (model.isCellEditable(table.convertRowIndexToModel(row), table.convertColumnIndexToModel(column))) {
|
||||||
|
it.setIdentity(selectedValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mapping.getIdentity() != null && mapping.getLanguage() != null) {
|
||||||
|
mapping.setState(SubtitleMapping.Status.UploadReady);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Logger.getLogger(SubtitleUploadDialog.class.getClass().getName()).log(Level.WARNING, e.getMessage(), e);
|
||||||
|
}
|
||||||
|
table.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUploadPlan(Map<File, File> uploadPlan) {
|
||||||
|
List<SubtitleMapping> mappings = new ArrayList<SubtitleMapping>(uploadPlan.size());
|
||||||
|
for (Entry<File, File> entry : uploadPlan.entrySet()) {
|
||||||
|
File subtitle = entry.getKey();
|
||||||
|
File video = entry.getValue();
|
||||||
|
|
||||||
|
Locale locale = MediaDetection.guessLanguageFromSuffix(subtitle);
|
||||||
|
Language language = Language.getLanguage(locale);
|
||||||
|
|
||||||
|
mappings.add(new SubtitleMapping(subtitle, video, language));
|
||||||
|
}
|
||||||
|
|
||||||
|
subtitleMappingTable.setModel(new SubtitleMappingTableModel(mappings.toArray(new SubtitleMapping[0])));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startChecking() {
|
||||||
|
checkExecutorService = Executors.newFixedThreadPool(2);
|
||||||
|
|
||||||
|
SubtitleMapping[] data = ((SubtitleMappingTableModel) subtitleMappingTable.getModel()).getData();
|
||||||
|
for (SubtitleMapping it : data) {
|
||||||
|
if (it.getSubtitle() != null && it.getVideo() != null) {
|
||||||
|
checkExecutorService.submit(new CheckTask(it));
|
||||||
|
} else {
|
||||||
|
it.setState(SubtitleMapping.Status.IllegalInput);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
checkExecutorService.shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Action uploadAction = new AbstractAction("Upload", ResourceManager.getIcon("dialog.continue")) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent evt) {
|
||||||
|
// disable any active cell editor
|
||||||
|
if (subtitleMappingTable.getCellEditor() != null) {
|
||||||
|
subtitleMappingTable.getCellEditor().stopCellEditing();
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't allow restart of upload as long as there are still unfinished download tasks
|
||||||
|
if (uploadExecutorService != null && !uploadExecutorService.isTerminated()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uploadExecutorService = Executors.newFixedThreadPool(1);
|
||||||
|
|
||||||
|
SubtitleMapping[] data = ((SubtitleMappingTableModel) subtitleMappingTable.getModel()).getData();
|
||||||
|
for (final SubtitleMapping it : data) {
|
||||||
|
if (it.getStatus() == SubtitleMapping.Status.UploadReady) {
|
||||||
|
uploadExecutorService.submit(new UploadTask(it));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// terminate after all uploads have been completed
|
||||||
|
uploadExecutorService.shutdown();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final Action finishAction = new AbstractAction("Close", ResourceManager.getIcon("dialog.cancel")) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent evt) {
|
||||||
|
if (checkExecutorService != null) {
|
||||||
|
checkExecutorService.shutdownNow();
|
||||||
|
}
|
||||||
|
if (uploadExecutorService != null) {
|
||||||
|
uploadExecutorService.shutdownNow();
|
||||||
|
}
|
||||||
|
|
||||||
|
setVisible(false);
|
||||||
|
dispose();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private class MovieRenderer extends DefaultTableCellRenderer {
|
||||||
|
@Override
|
||||||
|
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
|
||||||
|
super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
||||||
|
String text = null;
|
||||||
|
String tooltip = null;
|
||||||
|
Icon icon = null;
|
||||||
|
|
||||||
|
Movie movie = (Movie) value;
|
||||||
|
if (movie != null) {
|
||||||
|
text = movie.toString();
|
||||||
|
tooltip = String.format("%s [tt%07d]", movie.toString(), movie.getImdbId());
|
||||||
|
icon = database.getIcon();
|
||||||
|
}
|
||||||
|
|
||||||
|
setText(text);
|
||||||
|
setToolTipText(tooltip);
|
||||||
|
setIcon(icon);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class FileRenderer extends DefaultTableCellRenderer {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
|
||||||
|
super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
||||||
|
String text = null;
|
||||||
|
String tooltip = null;
|
||||||
|
Icon icon = null;
|
||||||
|
|
||||||
|
if (value != null) {
|
||||||
|
File file = (File) value;
|
||||||
|
text = file.getName();
|
||||||
|
tooltip = file.getPath();
|
||||||
|
if (SUBTITLE_FILES.accept(file)) {
|
||||||
|
icon = ResourceManager.getIcon("file.subtitle");
|
||||||
|
} else if (VIDEO_FILES.accept(file)) {
|
||||||
|
icon = ResourceManager.getIcon("file.video");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setText(text);
|
||||||
|
setToolTipText(text);
|
||||||
|
setIcon(icon);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class LanguageRenderer implements TableCellRenderer, ListCellRenderer {
|
||||||
|
|
||||||
|
private DefaultTableCellRenderer tableCell = new DefaultTableCellRenderer();
|
||||||
|
private DefaultListCellRenderer listCell = new DefaultListCellRenderer();
|
||||||
|
|
||||||
|
private Component configure(JLabel c, Object value, boolean isSelected, boolean hasFocus) {
|
||||||
|
String text = null;
|
||||||
|
Icon icon = null;
|
||||||
|
|
||||||
|
if (value != null) {
|
||||||
|
Language language = (Language) value;
|
||||||
|
text = language.getName();
|
||||||
|
icon = ResourceManager.getFlagIcon(language.getCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
c.setText(text);
|
||||||
|
c.setIcon(icon);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
|
||||||
|
return configure((DefaultTableCellRenderer) tableCell.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column), value, isSelected, hasFocus);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
|
||||||
|
return configure((DefaultListCellRenderer) listCell.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus), value, isSelected, cellHasFocus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class StatusRenderer extends DefaultTableCellRenderer {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
|
||||||
|
super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
||||||
|
String text = null;
|
||||||
|
Icon icon = null;
|
||||||
|
|
||||||
|
// CheckPending, Checking, CheckFailed, AlreadyExists, Identifying, IdentificationRequired, UploadPending, Uploading, UploadComplete, UploadFailed;
|
||||||
|
switch ((SubtitleMapping.Status) value) {
|
||||||
|
case IllegalInput:
|
||||||
|
text = "No video/subtitle pair";
|
||||||
|
icon = ResourceManager.getIcon("status.error");
|
||||||
|
break;
|
||||||
|
case CheckPending:
|
||||||
|
text = "Pending...";
|
||||||
|
icon = ResourceManager.getIcon("worker.pending");
|
||||||
|
break;
|
||||||
|
case Checking:
|
||||||
|
text = "Checking database...";
|
||||||
|
icon = ResourceManager.getIcon("database.go");
|
||||||
|
break;
|
||||||
|
case CheckFailed:
|
||||||
|
text = "Failed to check database";
|
||||||
|
icon = ResourceManager.getIcon("database.error");
|
||||||
|
break;
|
||||||
|
case AlreadyExists:
|
||||||
|
text = "Subtitle already exists in database";
|
||||||
|
icon = ResourceManager.getIcon("database.ok");
|
||||||
|
break;
|
||||||
|
case Identifying:
|
||||||
|
text = "Auto-detect missing information";
|
||||||
|
icon = ResourceManager.getIcon("action.export");
|
||||||
|
break;
|
||||||
|
case IdentificationRequired:
|
||||||
|
text = "Unable to auto-detect movie / series info";
|
||||||
|
icon = ResourceManager.getIcon("dialog.continue.invalid");
|
||||||
|
break;
|
||||||
|
case UploadReady:
|
||||||
|
text = "Ready for upload";
|
||||||
|
icon = ResourceManager.getIcon("dialog.continue");
|
||||||
|
break;
|
||||||
|
case Uploading:
|
||||||
|
text = "Uploading...";
|
||||||
|
icon = ResourceManager.getIcon("database.go");
|
||||||
|
break;
|
||||||
|
case UploadComplete:
|
||||||
|
text = "Upload successful";
|
||||||
|
icon = ResourceManager.getIcon("database.ok");
|
||||||
|
break;
|
||||||
|
case UploadFailed:
|
||||||
|
text = "Upload failed";
|
||||||
|
icon = ResourceManager.getIcon("database.error");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
setText(text);
|
||||||
|
setIcon(icon);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SubtitleMappingTableModel extends AbstractTableModel {
|
||||||
|
|
||||||
|
private final SubtitleMapping[] data;
|
||||||
|
|
||||||
|
public SubtitleMappingTableModel(SubtitleMapping... mappings) {
|
||||||
|
this.data = mappings.clone();
|
||||||
|
|
||||||
|
for (int i = 0; i < data.length; i++) {
|
||||||
|
data[i].addPropertyChangeListener(new SubtitleMappingListener(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public SubtitleMapping[] getData() {
|
||||||
|
return data.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getColumnCount() {
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getColumnName(int column) {
|
||||||
|
switch (column) {
|
||||||
|
case 0:
|
||||||
|
return "Movie / Series";
|
||||||
|
case 1:
|
||||||
|
return "Video";
|
||||||
|
case 2:
|
||||||
|
return "Subtitle";
|
||||||
|
case 3:
|
||||||
|
return "Language";
|
||||||
|
case 4:
|
||||||
|
return "Status";
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getRowCount() {
|
||||||
|
return data.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getValueAt(int row, int column) {
|
||||||
|
switch (column) {
|
||||||
|
case 0:
|
||||||
|
return data[row].getIdentity();
|
||||||
|
case 1:
|
||||||
|
return data[row].getVideo();
|
||||||
|
case 2:
|
||||||
|
return data[row].getSubtitle();
|
||||||
|
case 3:
|
||||||
|
return data[row].getLanguage();
|
||||||
|
case 4:
|
||||||
|
return data[row].getStatus();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setValueAt(Object value, int row, int column) {
|
||||||
|
if (getColumnClass(column) == Language.class && value instanceof Language) {
|
||||||
|
data[row].setLanguage((Language) value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCellEditable(int row, int column) {
|
||||||
|
return (EnumSet.of(SubtitleMapping.Status.IdentificationRequired, SubtitleMapping.Status.UploadReady).contains(data[row].getStatus())) && (getColumnClass(column) == Movie.class || getColumnClass(column) == Language.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> getColumnClass(int column) {
|
||||||
|
switch (column) {
|
||||||
|
case 0:
|
||||||
|
return Movie.class;
|
||||||
|
case 1:
|
||||||
|
return File.class;
|
||||||
|
case 2:
|
||||||
|
return File.class;
|
||||||
|
case 3:
|
||||||
|
return Language.class;
|
||||||
|
case 4:
|
||||||
|
return SubtitleMapping.Status.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
fireTableRowsUpdated(index, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SubtitleMapping extends AbstractBean {
|
||||||
|
|
||||||
|
enum Status {
|
||||||
|
IllegalInput, CheckPending, Checking, CheckFailed, AlreadyExists, Identifying, IdentificationRequired, UploadReady, Uploading, UploadComplete, UploadFailed;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object identity;
|
||||||
|
private File subtitle;
|
||||||
|
private File video;
|
||||||
|
private Language language;
|
||||||
|
|
||||||
|
private Status status = Status.CheckPending;
|
||||||
|
private String message = null;
|
||||||
|
|
||||||
|
public SubtitleMapping(File subtitle, File video, Language language) {
|
||||||
|
this.subtitle = subtitle;
|
||||||
|
this.video = video;
|
||||||
|
this.language = language;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getIdentity() {
|
||||||
|
return identity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public File getSubtitle() {
|
||||||
|
return subtitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public File getVideo() {
|
||||||
|
return video;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Language getLanguage() {
|
||||||
|
return language;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Status getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIdentity(Object identity) {
|
||||||
|
this.identity = identity;
|
||||||
|
firePropertyChange("identity", null, this.identity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLanguage(Language language) {
|
||||||
|
this.language = language;
|
||||||
|
firePropertyChange("language", null, this.language);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setState(Status status) {
|
||||||
|
this.status = status;
|
||||||
|
firePropertyChange("status", null, this.status);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private class CheckTask extends SwingWorker<Object, Void> {
|
||||||
|
|
||||||
|
private final SubtitleMapping mapping;
|
||||||
|
|
||||||
|
public CheckTask(SubtitleMapping mapping) {
|
||||||
|
this.mapping = mapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Object doInBackground() throws Exception {
|
||||||
|
try {
|
||||||
|
mapping.setState(SubtitleMapping.Status.Checking);
|
||||||
|
CheckResult checkResult = database.checkSubtitle(mapping.getVideo(), mapping.getSubtitle());
|
||||||
|
|
||||||
|
// accept identity hint from search result
|
||||||
|
mapping.setIdentity(checkResult.identity);
|
||||||
|
|
||||||
|
if (checkResult.exists) {
|
||||||
|
mapping.setLanguage(Language.getLanguage(checkResult.language)); // trust language hint only if upload not required
|
||||||
|
mapping.setState(SubtitleMapping.Status.AlreadyExists);
|
||||||
|
return checkResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mapping.getLanguage() == null) {
|
||||||
|
mapping.setState(SubtitleMapping.Status.Identifying);
|
||||||
|
try {
|
||||||
|
Locale locale = database.detectLanguage(FileUtilities.readFile(mapping.getSubtitle()));
|
||||||
|
mapping.setLanguage(Language.getLanguage(locale));
|
||||||
|
} catch (Exception e) {
|
||||||
|
Logger.getLogger(CheckTask.class.getClass().getName()).log(Level.WARNING, "Failed to auto-detect language: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// default to English
|
||||||
|
if (mapping.getLanguage() == null) {
|
||||||
|
mapping.setLanguage(Language.getLanguage("en"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mapping.getIdentity() == null) {
|
||||||
|
mapping.setState(SubtitleMapping.Status.Identifying);
|
||||||
|
try {
|
||||||
|
Collection<Movie> identity = MediaDetection.detectMovie(mapping.getVideo(), database, database, Locale.ENGLISH, true);
|
||||||
|
for (Movie it : identity) {
|
||||||
|
if (it.getImdbId() <= 0 && it.getTmdbId() > 0) {
|
||||||
|
it = MediaDetection.tmdb2imdb(it);
|
||||||
|
}
|
||||||
|
if (it != null && it.getImdbId() > 0) {
|
||||||
|
mapping.setIdentity(it);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Logger.getLogger(CheckTask.class.getClass().getName()).log(Level.WARNING, "Failed to auto-detect movie: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mapping.getIdentity() == null) {
|
||||||
|
mapping.setState(SubtitleMapping.Status.IdentificationRequired);
|
||||||
|
} else {
|
||||||
|
mapping.setState(SubtitleMapping.Status.UploadReady);
|
||||||
|
}
|
||||||
|
|
||||||
|
return checkResult;
|
||||||
|
} catch (Exception e) {
|
||||||
|
Logger.getLogger(CheckTask.class.getClass().getName()).log(Level.SEVERE, e.getMessage(), e);
|
||||||
|
mapping.setState(SubtitleMapping.Status.CheckFailed);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class UploadTask extends SwingWorker<Object, Void> {
|
||||||
|
|
||||||
|
private final SubtitleMapping mapping;
|
||||||
|
|
||||||
|
public UploadTask(SubtitleMapping mapping) {
|
||||||
|
this.mapping = mapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Object doInBackground() {
|
||||||
|
try {
|
||||||
|
mapping.setState(SubtitleMapping.Status.Uploading);
|
||||||
|
if (true)
|
||||||
|
throw new RuntimeException();
|
||||||
|
|
||||||
|
database.uploadSubtitle(mapping.getIdentity(), mapping.getLanguage().toLocale(), mapping.getVideo(), mapping.getSubtitle());
|
||||||
|
mapping.setState(SubtitleMapping.Status.UploadComplete);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Logger.getLogger(UploadTask.class.getClass().getName()).log(Level.SEVERE, e.getMessage(), e);
|
||||||
|
mapping.setState(SubtitleMapping.Status.UploadFailed);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,7 +1,5 @@
|
||||||
|
|
||||||
package net.sourceforge.filebot.web;
|
package net.sourceforge.filebot.web;
|
||||||
|
|
||||||
|
|
||||||
import static java.lang.Math.*;
|
import static java.lang.Math.*;
|
||||||
import static java.util.Arrays.*;
|
import static java.util.Arrays.*;
|
||||||
import static java.util.Collections.*;
|
import static java.util.Collections.*;
|
||||||
|
@ -43,52 +41,51 @@ import net.sourceforge.filebot.web.OpenSubtitlesXmlRpc.TryUploadResponse;
|
||||||
import net.sourceforge.tuned.Timer;
|
import net.sourceforge.tuned.Timer;
|
||||||
import redstone.xmlrpc.XmlRpcException;
|
import redstone.xmlrpc.XmlRpcException;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SubtitleClient for OpenSubtitles.
|
* SubtitleClient for OpenSubtitles.
|
||||||
*/
|
*/
|
||||||
public class OpenSubtitlesClient implements SubtitleProvider, VideoHashSubtitleService, MovieIdentificationService {
|
public class OpenSubtitlesClient implements SubtitleProvider, VideoHashSubtitleService, MovieIdentificationService {
|
||||||
|
|
||||||
private final OpenSubtitlesXmlRpc xmlrpc;
|
public final OpenSubtitlesXmlRpc xmlrpc;
|
||||||
|
|
||||||
private String username = "";
|
private String username = "";
|
||||||
private String password = "";
|
private String password = "";
|
||||||
|
|
||||||
|
|
||||||
public OpenSubtitlesClient(String useragent) {
|
public OpenSubtitlesClient(String useragent) {
|
||||||
this.xmlrpc = new OpenSubtitlesXmlRpc(useragent);
|
this.xmlrpc = new OpenSubtitlesXmlRpc(useragent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public synchronized void setUser(String username, String password) {
|
||||||
|
// cancel previous session
|
||||||
|
this.logout();
|
||||||
|
|
||||||
public void setUser(String username, String password) {
|
|
||||||
this.username = username;
|
this.username = username;
|
||||||
this.password = password;
|
this.password = password;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isAnonymous() {
|
||||||
|
return username.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return "OpenSubtitles";
|
return "OpenSubtitles";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public URI getLink() {
|
public URI getLink() {
|
||||||
return URI.create("http://www.opensubtitles.org");
|
return URI.create("http://www.opensubtitles.org");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Icon getIcon() {
|
public Icon getIcon() {
|
||||||
return ResourceManager.getIcon("search.opensubtitles");
|
return ResourceManager.getIcon("search.opensubtitles");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public ResultCache getCache() {
|
public ResultCache getCache() {
|
||||||
return new ResultCache("opensubtitles.org", Cache.getCache("web-datasource"));
|
return new ResultCache("opensubtitles.org", Cache.getCache("web-datasource"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<SearchResult> search(String query) throws Exception {
|
public List<SearchResult> search(String query) throws Exception {
|
||||||
List<SearchResult> result = getCache().getSearchResult("search", query, null);
|
List<SearchResult> result = getCache().getSearchResult("search", query, null);
|
||||||
|
@ -112,7 +109,6 @@ public class OpenSubtitlesClient implements SubtitleProvider, VideoHashSubtitleS
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<SubtitleDescriptor> getSubtitleList(SearchResult searchResult, String languageName) throws Exception {
|
public List<SubtitleDescriptor> getSubtitleList(SearchResult searchResult, String languageName) throws Exception {
|
||||||
List<SubtitleDescriptor> subtitles = getCache().getSubtitleDescriptorList(searchResult, languageName);
|
List<SubtitleDescriptor> subtitles = getCache().getSubtitleDescriptorList(searchResult, languageName);
|
||||||
|
@ -134,7 +130,6 @@ public class OpenSubtitlesClient implements SubtitleProvider, VideoHashSubtitleS
|
||||||
return subtitles;
|
return subtitles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<File, List<SubtitleDescriptor>> getSubtitleList(File[] files, String languageName) throws Exception {
|
public Map<File, List<SubtitleDescriptor>> getSubtitleList(File[] files, String languageName) throws Exception {
|
||||||
// singleton array with or empty array
|
// singleton array with or empty array
|
||||||
|
@ -196,56 +191,85 @@ public class OpenSubtitlesClient implements SubtitleProvider, VideoHashSubtitleS
|
||||||
return resultMap;
|
return resultMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean publishSubtitle(int imdbid, String languageName, File[] videoFile, File[] subtitleFile) throws Exception {
|
public CheckResult checkSubtitle(File videoFile, File subtitleFile) throws Exception {
|
||||||
SubFile[] subs = new SubFile[subtitleFile.length];
|
// subhash (md5 of subtitles), subfilename, moviehash, moviebytesize, moviefilename
|
||||||
|
|
||||||
// subhash (md5 of subtitles), subfilename, moviehash, moviebytesize, moviefilename.
|
|
||||||
for (int i = 0; i < subtitleFile.length; i++) {
|
|
||||||
SubFile sub = new SubFile();
|
SubFile sub = new SubFile();
|
||||||
sub.setSubHash(md5(subtitleFile[i]));
|
sub.setSubHash(md5(subtitleFile));
|
||||||
sub.setSubFileName(subtitleFile[i].getName());
|
sub.setSubFileName(subtitleFile.getName());
|
||||||
sub.setMovieHash(computeHash(videoFile[i]));
|
sub.setMovieHash(computeHash(videoFile));
|
||||||
sub.setMovieByteSize(videoFile[i].length());
|
sub.setMovieByteSize(videoFile.length());
|
||||||
sub.setMovieFileName(videoFile[i].getName());
|
sub.setMovieFileName(videoFile.getName());
|
||||||
subs[i] = sub;
|
|
||||||
}
|
|
||||||
|
|
||||||
// require login
|
// require login
|
||||||
login();
|
login();
|
||||||
|
|
||||||
// check if subs already exist in db
|
// check if subs already exist in DB
|
||||||
TryUploadResponse response = xmlrpc.tryUploadSubtitles(subs);
|
TryUploadResponse response = xmlrpc.tryUploadSubtitles(sub);
|
||||||
System.out.println(response); // TODO only upload if necessary OR return false
|
|
||||||
|
// TryUploadResponse: false => [{HashWasAlreadyInDb=1, MovieKind=movie, IDSubtitle=3167446, MoviefilenameWasAlreadyInDb=1, ISO639=en, MovieYear=2007, SubLanguageID=eng, MovieName=Blades of Glory, MovieNameEng=, IDMovieImdb=445934}]
|
||||||
|
boolean exists = !response.isUploadRequired();
|
||||||
|
Movie identity = null;
|
||||||
|
Locale language = null;
|
||||||
|
|
||||||
|
if (response.getSubtitleData().size() > 0) {
|
||||||
|
try {
|
||||||
|
Map<String, String> fields = response.getSubtitleData().get(0);
|
||||||
|
|
||||||
|
String lang = fields.get("SubLanguageID");
|
||||||
|
language = new Locale(lang);
|
||||||
|
|
||||||
|
String imdb = fields.get("IDMovieImdb");
|
||||||
|
String name = fields.get("MovieName");
|
||||||
|
String year = fields.get("MovieYear");
|
||||||
|
identity = new Movie(name, Integer.parseInt(year), Integer.parseInt(imdb), -1);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Logger.getLogger(getClass().getName()).log(Level.WARNING, e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CheckResult(exists, identity, language);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void uploadSubtitle(Object identity, Locale language, File videoFile, File subtitleFile) throws Exception {
|
||||||
|
if (!(identity instanceof Movie && ((Movie) identity).getImdbId() > 0)) {
|
||||||
|
throw new IllegalArgumentException("Illegal Movie ID: " + identity);
|
||||||
|
}
|
||||||
|
|
||||||
|
int imdbid = ((Movie) identity).getImdbId();
|
||||||
|
String languageName = getSubLanguageID(language.getDisplayName(Locale.ENGLISH));
|
||||||
|
|
||||||
|
// subhash (md5 of subtitles), subfilename, moviehash, moviebytesize, moviefilename
|
||||||
|
SubFile sub = new SubFile();
|
||||||
|
sub.setSubHash(md5(subtitleFile));
|
||||||
|
sub.setSubFileName(subtitleFile.getName());
|
||||||
|
sub.setMovieHash(computeHash(videoFile));
|
||||||
|
sub.setMovieByteSize(videoFile.length());
|
||||||
|
sub.setMovieFileName(videoFile.getName());
|
||||||
|
|
||||||
BaseInfo info = new BaseInfo();
|
BaseInfo info = new BaseInfo();
|
||||||
info.setIDMovieImdb(imdbid);
|
info.setIDMovieImdb(imdbid);
|
||||||
info.setSubLanguageID(getSubLanguageID(languageName));
|
info.setSubLanguageID(languageName);
|
||||||
|
|
||||||
// encode subtitle contents
|
// encode subtitle contents
|
||||||
for (int i = 0; i < subtitleFile.length; i++) {
|
sub.setSubContent(readFile(subtitleFile));
|
||||||
// grab subtitle content
|
|
||||||
subs[i].setSubContent(readFile(subtitleFile[i]));
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// grab media info
|
|
||||||
MediaInfo mi = new MediaInfo();
|
MediaInfo mi = new MediaInfo();
|
||||||
mi.open(videoFile[i]);
|
mi.open(videoFile);
|
||||||
subs[i].setMovieFPS(mi.get(StreamKind.Video, 0, "FrameRate"));
|
sub.setMovieFPS(mi.get(StreamKind.Video, 0, "FrameRate"));
|
||||||
subs[i].setMovieTimeMS(mi.get(StreamKind.General, 0, "Duration"));
|
sub.setMovieTimeMS(mi.get(StreamKind.General, 0, "Duration"));
|
||||||
mi.close();
|
mi.close();
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
Logger.getLogger(getClass().getName()).log(Level.WARNING, e.getMessage(), e);
|
Logger.getLogger(getClass().getName()).log(Level.WARNING, e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
URI resource = xmlrpc.uploadSubtitles(info, subs);
|
URI resource = xmlrpc.uploadSubtitles(info, sub);
|
||||||
System.out.println(resource);
|
System.out.println(resource);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate MD5 hash.
|
* Calculate MD5 hash.
|
||||||
*/
|
*/
|
||||||
|
@ -259,7 +283,6 @@ public class OpenSubtitlesClient implements SubtitleProvider, VideoHashSubtitleS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||||
@Override
|
@Override
|
||||||
public List<Movie> searchMovie(String query, Locale locale) throws Exception {
|
public List<Movie> searchMovie(String query, Locale locale) throws Exception {
|
||||||
|
@ -277,7 +300,6 @@ public class OpenSubtitlesClient implements SubtitleProvider, VideoHashSubtitleS
|
||||||
return movies;
|
return movies;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Movie getMovieDescriptor(int imdbid, Locale locale) throws Exception {
|
public Movie getMovieDescriptor(int imdbid, Locale locale) throws Exception {
|
||||||
Movie result = getCache().getData("getMovieDescriptor", imdbid, locale, Movie.class);
|
Movie result = getCache().getData("getMovieDescriptor", imdbid, locale, Movie.class);
|
||||||
|
@ -294,12 +316,10 @@ public class OpenSubtitlesClient implements SubtitleProvider, VideoHashSubtitleS
|
||||||
return movie;
|
return movie;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Movie getMovieDescriptor(File movieFile, Locale locale) throws Exception {
|
public Movie getMovieDescriptor(File movieFile, Locale locale) throws Exception {
|
||||||
return getMovieDescriptors(singleton(movieFile), locale).get(movieFile);
|
return getMovieDescriptors(singleton(movieFile), locale).get(movieFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<File, Movie> getMovieDescriptors(Collection<File> movieFiles, Locale locale) throws Exception {
|
public Map<File, Movie> getMovieDescriptors(Collection<File> movieFiles, Locale locale) throws Exception {
|
||||||
// create result array
|
// create result array
|
||||||
|
@ -354,7 +374,6 @@ public class OpenSubtitlesClient implements SubtitleProvider, VideoHashSubtitleS
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public URI getSubtitleListLink(SearchResult searchResult, String languageName) {
|
public URI getSubtitleListLink(SearchResult searchResult, String languageName) {
|
||||||
Movie movie = (Movie) searchResult;
|
Movie movie = (Movie) searchResult;
|
||||||
|
@ -371,7 +390,6 @@ public class OpenSubtitlesClient implements SubtitleProvider, VideoHashSubtitleS
|
||||||
return URI.create(String.format("http://www.opensubtitles.org/en/search/imdbid-%d/sublanguageid-%s", movie.getImdbId(), sublanguageid));
|
return URI.create(String.format("http://www.opensubtitles.org/en/search/imdbid-%d/sublanguageid-%s", movie.getImdbId(), sublanguageid));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Locale detectLanguage(byte[] data) throws Exception {
|
public Locale detectLanguage(byte[] data) throws Exception {
|
||||||
// require login
|
// require login
|
||||||
login();
|
login();
|
||||||
|
@ -383,7 +401,6 @@ public class OpenSubtitlesClient implements SubtitleProvider, VideoHashSubtitleS
|
||||||
return languages.size() > 0 ? new Locale(languages.get(0)) : null;
|
return languages.size() > 0 ? new Locale(languages.get(0)) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public synchronized void login() throws Exception {
|
public synchronized void login() throws Exception {
|
||||||
if (!xmlrpc.isLoggedOn()) {
|
if (!xmlrpc.isLoggedOn()) {
|
||||||
xmlrpc.login(username, password, "en");
|
xmlrpc.login(username, password, "en");
|
||||||
|
@ -392,7 +409,6 @@ public class OpenSubtitlesClient implements SubtitleProvider, VideoHashSubtitleS
|
||||||
logoutTimer.set(10, TimeUnit.MINUTES, true);
|
logoutTimer.set(10, TimeUnit.MINUTES, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected synchronized void logout() {
|
protected synchronized void logout() {
|
||||||
if (xmlrpc.isLoggedOn()) {
|
if (xmlrpc.isLoggedOn()) {
|
||||||
try {
|
try {
|
||||||
|
@ -401,7 +417,6 @@ public class OpenSubtitlesClient implements SubtitleProvider, VideoHashSubtitleS
|
||||||
Logger.getLogger(getClass().getName()).log(Level.WARNING, "Logout failed", e);
|
Logger.getLogger(getClass().getName()).log(Level.WARNING, "Logout failed", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logoutTimer.cancel();
|
logoutTimer.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -413,7 +428,6 @@ public class OpenSubtitlesClient implements SubtitleProvider, VideoHashSubtitleS
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SubLanguageID by English language name
|
* SubLanguageID by English language name
|
||||||
*/
|
*/
|
||||||
|
@ -443,7 +457,6 @@ public class OpenSubtitlesClient implements SubtitleProvider, VideoHashSubtitleS
|
||||||
return subLanguageMap;
|
return subLanguageMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected String getSubLanguageID(String languageName) throws Exception {
|
protected String getSubLanguageID(String languageName) throws Exception {
|
||||||
Map<String, String> subLanguageMap = getSubLanguageMap();
|
Map<String, String> subLanguageMap = getSubLanguageMap();
|
||||||
String key = languageName.toLowerCase();
|
String key = languageName.toLowerCase();
|
||||||
|
@ -455,7 +468,6 @@ public class OpenSubtitlesClient implements SubtitleProvider, VideoHashSubtitleS
|
||||||
return subLanguageMap.get(key);
|
return subLanguageMap.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected String getLanguageName(String subLanguageID) throws Exception {
|
protected String getLanguageName(String subLanguageID) throws Exception {
|
||||||
for (Entry<String, String> it : getSubLanguageMap().entrySet()) {
|
for (Entry<String, String> it : getSubLanguageMap().entrySet()) {
|
||||||
if (it.getValue().equals(subLanguageID.toLowerCase()))
|
if (it.getValue().equals(subLanguageID.toLowerCase()))
|
||||||
|
@ -465,24 +477,20 @@ public class OpenSubtitlesClient implements SubtitleProvider, VideoHashSubtitleS
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected static class ResultCache {
|
protected static class ResultCache {
|
||||||
|
|
||||||
private final String id;
|
private final String id;
|
||||||
private final Cache cache;
|
private final Cache cache;
|
||||||
|
|
||||||
|
|
||||||
public ResultCache(String id, Cache cache) {
|
public ResultCache(String id, Cache cache) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.cache = cache;
|
this.cache = cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected String normalize(String query) {
|
protected String normalize(String query) {
|
||||||
return query == null ? null : query.trim().toLowerCase();
|
return query == null ? null : query.trim().toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public <T extends SearchResult> List<T> putSearchResult(String method, String query, Locale locale, List<T> value) {
|
public <T extends SearchResult> List<T> putSearchResult(String method, String query, Locale locale, List<T> value) {
|
||||||
try {
|
try {
|
||||||
cache.put(new Key(id, normalize(query)), value.toArray(new SearchResult[0]));
|
cache.put(new Key(id, normalize(query)), value.toArray(new SearchResult[0]));
|
||||||
|
@ -493,7 +501,6 @@ public class OpenSubtitlesClient implements SubtitleProvider, VideoHashSubtitleS
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public List<SearchResult> getSearchResult(String method, String query, Locale locale) {
|
public List<SearchResult> getSearchResult(String method, String query, Locale locale) {
|
||||||
try {
|
try {
|
||||||
|
@ -508,7 +515,6 @@ public class OpenSubtitlesClient implements SubtitleProvider, VideoHashSubtitleS
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public List<SubtitleDescriptor> putSubtitleDescriptorList(Object key, String locale, List<SubtitleDescriptor> subtitles) {
|
public List<SubtitleDescriptor> putSubtitleDescriptorList(Object key, String locale, List<SubtitleDescriptor> subtitles) {
|
||||||
try {
|
try {
|
||||||
cache.put(new Key(id, key, locale), subtitles.toArray(new SubtitleDescriptor[0]));
|
cache.put(new Key(id, key, locale), subtitles.toArray(new SubtitleDescriptor[0]));
|
||||||
|
@ -519,7 +525,6 @@ public class OpenSubtitlesClient implements SubtitleProvider, VideoHashSubtitleS
|
||||||
return subtitles;
|
return subtitles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public List<SubtitleDescriptor> getSubtitleDescriptorList(Object key, String locale) {
|
public List<SubtitleDescriptor> getSubtitleDescriptorList(Object key, String locale) {
|
||||||
try {
|
try {
|
||||||
SubtitleDescriptor[] descriptors = cache.get(new Key(id, key, locale), SubtitleDescriptor[].class);
|
SubtitleDescriptor[] descriptors = cache.get(new Key(id, key, locale), SubtitleDescriptor[].class);
|
||||||
|
@ -533,7 +538,6 @@ public class OpenSubtitlesClient implements SubtitleProvider, VideoHashSubtitleS
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void putData(Object category, Object key, Locale locale, Object object) {
|
public void putData(Object category, Object key, Locale locale, Object object) {
|
||||||
try {
|
try {
|
||||||
cache.put(new Key(id, category, locale, key), object);
|
cache.put(new Key(id, category, locale, key), object);
|
||||||
|
@ -542,7 +546,6 @@ public class OpenSubtitlesClient implements SubtitleProvider, VideoHashSubtitleS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public <T> T getData(Object category, Object key, Locale locale, Class<T> type) {
|
public <T> T getData(Object category, Object key, Locale locale, Class<T> type) {
|
||||||
try {
|
try {
|
||||||
T value = cache.get(new Key(id, category, locale, key), type);
|
T value = cache.get(new Key(id, category, locale, key), type);
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
|
|
||||||
package net.sourceforge.filebot.web;
|
package net.sourceforge.filebot.web;
|
||||||
|
|
||||||
|
|
||||||
import static java.util.Collections.*;
|
import static java.util.Collections.*;
|
||||||
import static net.sourceforge.tuned.StringUtilities.*;
|
import static net.sourceforge.tuned.StringUtilities.*;
|
||||||
|
|
||||||
|
@ -32,19 +30,16 @@ import redstone.xmlrpc.XmlRpcException;
|
||||||
import redstone.xmlrpc.XmlRpcFault;
|
import redstone.xmlrpc.XmlRpcFault;
|
||||||
import redstone.xmlrpc.util.Base64;
|
import redstone.xmlrpc.util.Base64;
|
||||||
|
|
||||||
|
|
||||||
public class OpenSubtitlesXmlRpc {
|
public class OpenSubtitlesXmlRpc {
|
||||||
|
|
||||||
private final String useragent;
|
private final String useragent;
|
||||||
|
|
||||||
private String token;
|
private String token;
|
||||||
|
|
||||||
|
|
||||||
public OpenSubtitlesXmlRpc(String useragent) {
|
public OpenSubtitlesXmlRpc(String useragent) {
|
||||||
this.useragent = useragent;
|
this.useragent = useragent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Login as anonymous user
|
* Login as anonymous user
|
||||||
*/
|
*/
|
||||||
|
@ -52,15 +47,15 @@ public class OpenSubtitlesXmlRpc {
|
||||||
login("", "", "en");
|
login("", "", "en");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This will login user. This method should be called always when starting talking with
|
* This will login user. This method should be called always when starting talking with server.
|
||||||
* server.
|
|
||||||
*
|
*
|
||||||
* @param username username (blank for anonymous user)
|
* @param username
|
||||||
* @param password password (blank for anonymous user)
|
* username (blank for anonymous user)
|
||||||
* @param language ISO639 2-letter codes as language and later communication will be done in this language if
|
* @param password
|
||||||
* applicable (error codes and so on).
|
* password (blank for anonymous user)
|
||||||
|
* @param language
|
||||||
|
* ISO639 2-letter codes as language and later communication will be done in this language if applicable (error codes and so on).
|
||||||
*/
|
*/
|
||||||
public synchronized void login(String username, String password, String language) throws XmlRpcFault {
|
public synchronized void login(String username, String password, String language) throws XmlRpcFault {
|
||||||
Map<?, ?> response = invoke("LogIn", username, password, language, useragent);
|
Map<?, ?> response = invoke("LogIn", username, password, language, useragent);
|
||||||
|
@ -69,7 +64,6 @@ public class OpenSubtitlesXmlRpc {
|
||||||
token = response.get("token").toString();
|
token = response.get("token").toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This will logout user (ends session id). Call this function is before closing the client program.
|
* This will logout user (ends session id). Call this function is before closing the client program.
|
||||||
*/
|
*/
|
||||||
|
@ -84,23 +78,19 @@ public class OpenSubtitlesXmlRpc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean isLoggedOn() {
|
public boolean isLoggedOn() {
|
||||||
return token != null;
|
return token != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public Map<String, String> getServerInfo() throws XmlRpcFault {
|
public Map<String, String> getServerInfo() throws XmlRpcFault {
|
||||||
return (Map<String, String>) invoke("ServerInfo", token);
|
return (Map<String, String>) invoke("ServerInfo", token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public List<OpenSubtitlesSubtitleDescriptor> searchSubtitles(int imdbid, String... sublanguageids) throws XmlRpcFault {
|
public List<OpenSubtitlesSubtitleDescriptor> searchSubtitles(int imdbid, String... sublanguageids) throws XmlRpcFault {
|
||||||
return searchSubtitles(singleton(Query.forImdbId(imdbid, sublanguageids)));
|
return searchSubtitles(singleton(Query.forImdbId(imdbid, sublanguageids)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public List<OpenSubtitlesSubtitleDescriptor> searchSubtitles(Collection<Query> queryList) throws XmlRpcFault {
|
public List<OpenSubtitlesSubtitleDescriptor> searchSubtitles(Collection<Query> queryList) throws XmlRpcFault {
|
||||||
List<OpenSubtitlesSubtitleDescriptor> subtitles = new ArrayList<OpenSubtitlesSubtitleDescriptor>();
|
List<OpenSubtitlesSubtitleDescriptor> subtitles = new ArrayList<OpenSubtitlesSubtitleDescriptor>();
|
||||||
|
@ -119,7 +109,6 @@ public class OpenSubtitlesXmlRpc {
|
||||||
return subtitles;
|
return subtitles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public List<Movie> searchMoviesOnIMDB(String query) throws XmlRpcFault {
|
public List<Movie> searchMoviesOnIMDB(String query) throws XmlRpcFault {
|
||||||
Map<?, ?> response = invoke("SearchMoviesOnIMDB", token, query);
|
Map<?, ?> response = invoke("SearchMoviesOnIMDB", token, query);
|
||||||
|
@ -153,7 +142,6 @@ public class OpenSubtitlesXmlRpc {
|
||||||
return movies;
|
return movies;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public TryUploadResponse tryUploadSubtitles(SubFile... subtitles) throws XmlRpcFault {
|
public TryUploadResponse tryUploadSubtitles(SubFile... subtitles) throws XmlRpcFault {
|
||||||
Map<String, SubFile> struct = new HashMap<String, SubFile>();
|
Map<String, SubFile> struct = new HashMap<String, SubFile>();
|
||||||
|
@ -165,7 +153,7 @@ public class OpenSubtitlesXmlRpc {
|
||||||
|
|
||||||
Map<?, ?> response = invoke("TryUploadSubtitles", token, struct);
|
Map<?, ?> response = invoke("TryUploadSubtitles", token, struct);
|
||||||
|
|
||||||
boolean uploadRequired = response.get("alreadyindb").equals("0");
|
boolean uploadRequired = response.get("alreadyindb").toString().equals("0");
|
||||||
List<Map<String, String>> subtitleData = new ArrayList<Map<String, String>>();
|
List<Map<String, String>> subtitleData = new ArrayList<Map<String, String>>();
|
||||||
|
|
||||||
if (response.get("data") instanceof Map) {
|
if (response.get("data") instanceof Map) {
|
||||||
|
@ -177,7 +165,6 @@ public class OpenSubtitlesXmlRpc {
|
||||||
return new TryUploadResponse(uploadRequired, subtitleData);
|
return new TryUploadResponse(uploadRequired, subtitleData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public URI uploadSubtitles(BaseInfo baseInfo, SubFile... subtitles) throws XmlRpcFault {
|
public URI uploadSubtitles(BaseInfo baseInfo, SubFile... subtitles) throws XmlRpcFault {
|
||||||
Map<String, Object> struct = new HashMap<String, Object>();
|
Map<String, Object> struct = new HashMap<String, Object>();
|
||||||
|
|
||||||
|
@ -195,7 +182,6 @@ public class OpenSubtitlesXmlRpc {
|
||||||
return URI.create(response.get("data").toString());
|
return URI.create(response.get("data").toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public List<String> detectLanguage(byte[] data) throws XmlRpcFault {
|
public List<String> detectLanguage(byte[] data) throws XmlRpcFault {
|
||||||
// compress and base64 encode
|
// compress and base64 encode
|
||||||
|
@ -211,7 +197,6 @@ public class OpenSubtitlesXmlRpc {
|
||||||
return languages;
|
return languages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public Map<String, Integer> checkSubHash(Collection<String> hashes) throws XmlRpcFault {
|
public Map<String, Integer> checkSubHash(Collection<String> hashes) throws XmlRpcFault {
|
||||||
Map<?, ?> response = invoke("CheckSubHash", token, hashes);
|
Map<?, ?> response = invoke("CheckSubHash", token, hashes);
|
||||||
|
@ -227,7 +212,6 @@ public class OpenSubtitlesXmlRpc {
|
||||||
return subHashMap;
|
return subHashMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public Map<String, Movie> checkMovieHash(Collection<String> hashes, int minSeenCount) throws XmlRpcFault {
|
public Map<String, Movie> checkMovieHash(Collection<String> hashes, int minSeenCount) throws XmlRpcFault {
|
||||||
Map<String, Movie> movieHashMap = new HashMap<String, Movie>();
|
Map<String, Movie> movieHashMap = new HashMap<String, Movie>();
|
||||||
|
@ -274,12 +258,10 @@ public class OpenSubtitlesXmlRpc {
|
||||||
return movieHashMap;
|
return movieHashMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Map<String, String> getSubLanguages() throws XmlRpcFault {
|
public Map<String, String> getSubLanguages() throws XmlRpcFault {
|
||||||
return getSubLanguages("en");
|
return getSubLanguages("en");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public Movie getIMDBMovieDetails(int imdbid) throws XmlRpcFault {
|
public Movie getIMDBMovieDetails(int imdbid) throws XmlRpcFault {
|
||||||
Map<?, ?> response = invoke("GetIMDBMovieDetails", token, imdbid);
|
Map<?, ?> response = invoke("GetIMDBMovieDetails", token, imdbid);
|
||||||
|
@ -299,7 +281,6 @@ public class OpenSubtitlesXmlRpc {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public Map<String, String> getSubLanguages(String languageCode) throws XmlRpcFault {
|
public Map<String, String> getSubLanguages(String languageCode) throws XmlRpcFault {
|
||||||
Map<String, List<Map<String, String>>> response = (Map<String, List<Map<String, String>>>) invoke("GetSubLanguages", languageCode);
|
Map<String, List<Map<String, String>>> response = (Map<String, List<Map<String, String>>>) invoke("GetSubLanguages", languageCode);
|
||||||
|
@ -313,12 +294,10 @@ public class OpenSubtitlesXmlRpc {
|
||||||
return subLanguageMap;
|
return subLanguageMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void noOperation() throws XmlRpcFault {
|
public void noOperation() throws XmlRpcFault {
|
||||||
invoke("NoOperation", token);
|
invoke("NoOperation", token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected Map<?, ?> invoke(String method, Object... arguments) throws XmlRpcFault {
|
protected Map<?, ?> invoke(String method, Object... arguments) throws XmlRpcFault {
|
||||||
try {
|
try {
|
||||||
XmlRpcClient rpc = new XmlRpcClient(getXmlRpcUrl(), false);
|
XmlRpcClient rpc = new XmlRpcClient(getXmlRpcUrl(), false);
|
||||||
|
@ -337,7 +316,6 @@ public class OpenSubtitlesXmlRpc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected URL getXmlRpcUrl() {
|
protected URL getXmlRpcUrl() {
|
||||||
try {
|
try {
|
||||||
return new URL("http://api.opensubtitles.org/xml-rpc");
|
return new URL("http://api.opensubtitles.org/xml-rpc");
|
||||||
|
@ -347,7 +325,6 @@ public class OpenSubtitlesXmlRpc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected static String encodeData(byte[] data) {
|
protected static String encodeData(byte[] data) {
|
||||||
try {
|
try {
|
||||||
DeflaterInputStream compressedDataStream = new DeflaterInputStream(new ByteArrayInputStream(data));
|
DeflaterInputStream compressedDataStream = new DeflaterInputStream(new ByteArrayInputStream(data));
|
||||||
|
@ -364,12 +341,13 @@ public class OpenSubtitlesXmlRpc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether status is OK or not
|
* Check whether status is OK or not
|
||||||
*
|
*
|
||||||
* @param status status code and message (e.g. 200 OK, 401 Unauthorized, ...)
|
* @param status
|
||||||
* @throws XmlRpcFault thrown if status code is not OK
|
* status code and message (e.g. 200 OK, 401 Unauthorized, ...)
|
||||||
|
* @throws XmlRpcFault
|
||||||
|
* thrown if status code is not OK
|
||||||
*/
|
*/
|
||||||
protected void checkResponse(Map<?, ?> response) throws XmlRpcFault {
|
protected void checkResponse(Map<?, ?> response) throws XmlRpcFault {
|
||||||
String status = (String) response.get("status");
|
String status = (String) response.get("status");
|
||||||
|
@ -386,7 +364,6 @@ public class OpenSubtitlesXmlRpc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static final class Query extends HashMap<String, Object> implements Serializable {
|
public static final class Query extends HashMap<String, Object> implements Serializable {
|
||||||
|
|
||||||
private Query(String imdbid, String... sublanguageids) {
|
private Query(String imdbid, String... sublanguageids) {
|
||||||
|
@ -394,99 +371,82 @@ public class OpenSubtitlesXmlRpc {
|
||||||
put("sublanguageid", join(sublanguageids, ","));
|
put("sublanguageid", join(sublanguageids, ","));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private Query(String moviehash, String moviebytesize, String... sublanguageids) {
|
private Query(String moviehash, String moviebytesize, String... sublanguageids) {
|
||||||
put("moviehash", moviehash);
|
put("moviehash", moviehash);
|
||||||
put("moviebytesize", moviebytesize);
|
put("moviebytesize", moviebytesize);
|
||||||
put("sublanguageid", join(sublanguageids, ","));
|
put("sublanguageid", join(sublanguageids, ","));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static Query forHash(String moviehash, long moviebytesize, String... sublanguageids) {
|
public static Query forHash(String moviehash, long moviebytesize, String... sublanguageids) {
|
||||||
return new Query(moviehash, Long.toString(moviebytesize), sublanguageids);
|
return new Query(moviehash, Long.toString(moviebytesize), sublanguageids);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static Query forImdbId(int imdbid, String... sublanguageids) {
|
public static Query forImdbId(int imdbid, String... sublanguageids) {
|
||||||
return new Query(Integer.toString(imdbid), sublanguageids);
|
return new Query(Integer.toString(imdbid), sublanguageids);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static final class BaseInfo extends HashMap<String, Object> {
|
public static final class BaseInfo extends HashMap<String, Object> {
|
||||||
|
|
||||||
public void setIDMovieImdb(int imdb) {
|
public void setIDMovieImdb(int imdb) {
|
||||||
put("idmovieimdb", Integer.toString(imdb));
|
put("idmovieimdb", Integer.toString(imdb));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setSubLanguageID(String sublanguageid) {
|
public void setSubLanguageID(String sublanguageid) {
|
||||||
put("sublanguageid", sublanguageid);
|
put("sublanguageid", sublanguageid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setMovieReleaseName(String moviereleasename) {
|
public void setMovieReleaseName(String moviereleasename) {
|
||||||
put("moviereleasename", moviereleasename);
|
put("moviereleasename", moviereleasename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setMovieAka(String movieaka) {
|
public void setMovieAka(String movieaka) {
|
||||||
put("movieaka", movieaka);
|
put("movieaka", movieaka);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setSubAuthorComment(String subauthorcomment) {
|
public void setSubAuthorComment(String subauthorcomment) {
|
||||||
put("subauthorcomment", subauthorcomment);
|
put("subauthorcomment", subauthorcomment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static final class SubFile extends HashMap<String, Object> {
|
public static final class SubFile extends HashMap<String, Object> {
|
||||||
|
|
||||||
public void setSubHash(String subhash) {
|
public void setSubHash(String subhash) {
|
||||||
put("subhash", subhash);
|
put("subhash", subhash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setSubFileName(String subfilename) {
|
public void setSubFileName(String subfilename) {
|
||||||
put("subfilename", subfilename);
|
put("subfilename", subfilename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setMovieHash(String moviehash) {
|
public void setMovieHash(String moviehash) {
|
||||||
put("moviehash", moviehash);
|
put("moviehash", moviehash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setMovieByteSize(long moviebytesize) {
|
public void setMovieByteSize(long moviebytesize) {
|
||||||
put("moviebytesize", Long.toString(moviebytesize));
|
put("moviebytesize", Long.toString(moviebytesize));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setMovieFileName(String moviefilename) {
|
public void setMovieFileName(String moviefilename) {
|
||||||
put("moviefilename", moviefilename);
|
put("moviefilename", moviefilename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setSubContent(byte[] data) {
|
public void setSubContent(byte[] data) {
|
||||||
put("subcontent", encodeData(data));
|
put("subcontent", encodeData(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setMovieTimeMS(String movietimems) {
|
public void setMovieTimeMS(String movietimems) {
|
||||||
if (movietimems.length() > 0) {
|
if (movietimems.length() > 0) {
|
||||||
put("movietimems", movietimems);
|
put("movietimems", movietimems);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setMovieFPS(String moviefps) {
|
public void setMovieFPS(String moviefps) {
|
||||||
if (moviefps.length() > 0) {
|
if (moviefps.length() > 0) {
|
||||||
put("moviefps", moviefps);
|
put("moviefps", moviefps);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setMovieFrames(String movieframes) {
|
public void setMovieFrames(String movieframes) {
|
||||||
if (movieframes.length() > 0) {
|
if (movieframes.length() > 0) {
|
||||||
put("movieframes", movieframes);
|
put("movieframes", movieframes);
|
||||||
|
@ -495,30 +455,25 @@ public class OpenSubtitlesXmlRpc {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static final class TryUploadResponse {
|
public static final class TryUploadResponse {
|
||||||
|
|
||||||
private final boolean uploadRequired;
|
private final boolean uploadRequired;
|
||||||
|
|
||||||
private final List<Map<String, String>> subtitleData;
|
private final List<Map<String, String>> subtitleData;
|
||||||
|
|
||||||
|
|
||||||
private TryUploadResponse(boolean uploadRequired, List<Map<String, String>> subtitleData) {
|
private TryUploadResponse(boolean uploadRequired, List<Map<String, String>> subtitleData) {
|
||||||
this.uploadRequired = uploadRequired;
|
this.uploadRequired = uploadRequired;
|
||||||
this.subtitleData = subtitleData;
|
this.subtitleData = subtitleData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean isUploadRequired() {
|
public boolean isUploadRequired() {
|
||||||
return uploadRequired;
|
return uploadRequired;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public List<Map<String, String>> getSubtitleData() {
|
public List<Map<String, String>> getSubtitleData() {
|
||||||
return subtitleData;
|
return subtitleData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return String.format("TryUploadResponse: %s => %s", uploadRequired, subtitleData);
|
return String.format("TryUploadResponse: %s => %s", uploadRequired, subtitleData);
|
||||||
|
|
|
@ -1,29 +1,42 @@
|
||||||
|
|
||||||
package net.sourceforge.filebot.web;
|
package net.sourceforge.filebot.web;
|
||||||
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
|
|
||||||
|
|
||||||
public interface VideoHashSubtitleService {
|
public interface VideoHashSubtitleService {
|
||||||
|
|
||||||
public Map<File, List<SubtitleDescriptor>> getSubtitleList(File[] videoFiles, String languageName) throws Exception;
|
public Map<File, List<SubtitleDescriptor>> getSubtitleList(File[] videoFiles, String languageName) throws Exception;
|
||||||
|
|
||||||
|
|
||||||
public boolean publishSubtitle(int imdbid, String languageName, File[] videoFile, File[] subtitleFile) throws Exception;
|
|
||||||
|
|
||||||
|
|
||||||
public String getName();
|
public String getName();
|
||||||
|
|
||||||
|
|
||||||
public URI getLink();
|
public URI getLink();
|
||||||
|
|
||||||
|
|
||||||
public Icon getIcon();
|
public Icon getIcon();
|
||||||
|
|
||||||
|
public CheckResult checkSubtitle(File videoFile, File subtitleFile) throws Exception;
|
||||||
|
|
||||||
|
public void uploadSubtitle(Object identity, Locale locale, File videoFile, File subtitleFile) throws Exception;
|
||||||
|
|
||||||
|
public static class CheckResult {
|
||||||
|
public final boolean exists;
|
||||||
|
public final Object identity;
|
||||||
|
public final Locale language;
|
||||||
|
|
||||||
|
public CheckResult(boolean exists, Object identity, Locale language) {
|
||||||
|
this.exists = exists;
|
||||||
|
this.identity = identity;
|
||||||
|
this.language = language;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format("%s [%s] => %s", identity, language, exists);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
|
|
||||||
package net.sourceforge.tuned;
|
package net.sourceforge.tuned;
|
||||||
|
|
||||||
|
|
||||||
import java.util.concurrent.ScheduledFuture;
|
import java.util.concurrent.ScheduledFuture;
|
||||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||||
import java.util.concurrent.ThreadFactory;
|
import java.util.concurrent.ThreadFactory;
|
||||||
|
@ -9,7 +7,6 @@ import java.util.concurrent.TimeUnit;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
|
||||||
public abstract class Timer implements Runnable {
|
public abstract class Timer implements Runnable {
|
||||||
|
|
||||||
private final ThreadFactory threadFactory = new DefaultThreadFactory("Timer", Thread.NORM_PRIORITY, true);
|
private final ThreadFactory threadFactory = new DefaultThreadFactory("Timer", Thread.NORM_PRIORITY, true);
|
||||||
|
@ -18,7 +15,6 @@ public abstract class Timer implements Runnable {
|
||||||
private ScheduledFuture<?> scheduledFuture;
|
private ScheduledFuture<?> scheduledFuture;
|
||||||
private Thread shutdownHook;
|
private Thread shutdownHook;
|
||||||
|
|
||||||
|
|
||||||
public synchronized void set(long delay, TimeUnit unit, boolean runBeforeShutdown) {
|
public synchronized void set(long delay, TimeUnit unit, boolean runBeforeShutdown) {
|
||||||
// create executor if necessary
|
// create executor if necessary
|
||||||
if (executor == null) {
|
if (executor == null) {
|
||||||
|
@ -65,18 +61,16 @@ public abstract class Timer implements Runnable {
|
||||||
scheduledFuture = executor.schedule(runnable, delay, unit);
|
scheduledFuture = executor.schedule(runnable, delay, unit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public synchronized void cancel() {
|
public synchronized void cancel() {
|
||||||
removeShutdownHook();
|
removeShutdownHook();
|
||||||
|
if (executor != null) {
|
||||||
// stop executor
|
|
||||||
executor.shutdownNow();
|
executor.shutdownNow();
|
||||||
|
}
|
||||||
|
|
||||||
scheduledFuture = null;
|
scheduledFuture = null;
|
||||||
executor = null;
|
executor = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private synchronized void addShutdownHook() {
|
private synchronized void addShutdownHook() {
|
||||||
if (shutdownHook == null) {
|
if (shutdownHook == null) {
|
||||||
shutdownHook = new Thread(this);
|
shutdownHook = new Thread(this);
|
||||||
|
@ -84,7 +78,6 @@ public abstract class Timer implements Runnable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private synchronized void removeShutdownHook() {
|
private synchronized void removeShutdownHook() {
|
||||||
if (shutdownHook != null) {
|
if (shutdownHook != null) {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
|
|
||||||
package net.sourceforge.tuned.ui;
|
package net.sourceforge.tuned.ui;
|
||||||
|
|
||||||
|
|
||||||
import static java.util.Collections.*;
|
import static java.util.Collections.*;
|
||||||
import static javax.swing.JOptionPane.*;
|
import static javax.swing.JOptionPane.*;
|
||||||
|
|
||||||
|
@ -39,19 +37,16 @@ import javax.swing.plaf.basic.BasicTableUI;
|
||||||
import javax.swing.text.JTextComponent;
|
import javax.swing.text.JTextComponent;
|
||||||
import javax.swing.undo.UndoManager;
|
import javax.swing.undo.UndoManager;
|
||||||
|
|
||||||
|
|
||||||
public final class TunedUtilities {
|
public final class TunedUtilities {
|
||||||
|
|
||||||
public static final Color TRANSLUCENT = new Color(255, 255, 255, 0);
|
public static final Color TRANSLUCENT = new Color(255, 255, 255, 0);
|
||||||
|
|
||||||
|
|
||||||
public static void checkEventDispatchThread() {
|
public static void checkEventDispatchThread() {
|
||||||
if (!SwingUtilities.isEventDispatchThread()) {
|
if (!SwingUtilities.isEventDispatchThread()) {
|
||||||
throw new IllegalStateException("Method must be accessed from the Swing Event Dispatch Thread, but was called on Thread \"" + Thread.currentThread().getName() + "\"");
|
throw new IllegalStateException("Method must be accessed from the Swing Event Dispatch Thread, but was called on Thread \"" + Thread.currentThread().getName() + "\"");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static Color interpolateHSB(Color c1, Color c2, float f) {
|
public static Color interpolateHSB(Color c1, Color c2, float f) {
|
||||||
float[] hsb1 = Color.RGBtoHSB(c1.getRed(), c1.getGreen(), c1.getBlue(), null);
|
float[] hsb1 = Color.RGBtoHSB(c1.getRed(), c1.getGreen(), c1.getBlue(), null);
|
||||||
float[] hsb2 = Color.RGBtoHSB(c2.getRed(), c2.getGreen(), c2.getBlue(), null);
|
float[] hsb2 = Color.RGBtoHSB(c2.getRed(), c2.getGreen(), c2.getBlue(), null);
|
||||||
|
@ -64,7 +59,6 @@ public final class TunedUtilities {
|
||||||
return Color.getHSBColor(hsb[0], hsb[1], hsb[2]);
|
return Color.getHSBColor(hsb[0], hsb[1], hsb[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static String escapeHTML(String s) {
|
public static String escapeHTML(String s) {
|
||||||
char[] sc = new char[] { '&', '<', '>', '"', '\'' };
|
char[] sc = new char[] { '&', '<', '>', '"', '\'' };
|
||||||
for (char c : sc) {
|
for (char c : sc) {
|
||||||
|
@ -73,22 +67,18 @@ public final class TunedUtilities {
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static Color derive(Color color, float alpha) {
|
public static Color derive(Color color, float alpha) {
|
||||||
return new Color(((int) ((alpha * 255)) << 24) | (color.getRGB() & 0x00FFFFFF), true);
|
return new Color(((int) ((alpha * 255)) << 24) | (color.getRGB() & 0x00FFFFFF), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static boolean isShiftOrAltDown(ActionEvent evt) {
|
public static boolean isShiftOrAltDown(ActionEvent evt) {
|
||||||
return checkModifiers(evt.getModifiers(), ActionEvent.SHIFT_MASK) || checkModifiers(evt.getModifiers(), ActionEvent.ALT_MASK);
|
return checkModifiers(evt.getModifiers(), ActionEvent.SHIFT_MASK) || checkModifiers(evt.getModifiers(), ActionEvent.ALT_MASK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static boolean checkModifiers(int modifiers, int mask) {
|
public static boolean checkModifiers(int modifiers, int mask) {
|
||||||
return ((modifiers & mask) == mask);
|
return ((modifiers & mask) == mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static JButton createImageButton(Action action) {
|
public static JButton createImageButton(Action action) {
|
||||||
JButton button = new JButton(action);
|
JButton button = new JButton(action);
|
||||||
button.setHideActionText(true);
|
button.setHideActionText(true);
|
||||||
|
@ -97,7 +87,6 @@ public final class TunedUtilities {
|
||||||
return button;
|
return button;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void installAction(JComponent component, KeyStroke keystroke, Action action) {
|
public static void installAction(JComponent component, KeyStroke keystroke, Action action) {
|
||||||
Object key = action.getValue(Action.NAME);
|
Object key = action.getValue(Action.NAME);
|
||||||
|
|
||||||
|
@ -108,7 +97,6 @@ public final class TunedUtilities {
|
||||||
component.getActionMap().put(key, action);
|
component.getActionMap().put(key, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static UndoManager installUndoSupport(JTextComponent component) {
|
public static UndoManager installUndoSupport(JTextComponent component) {
|
||||||
final UndoManager undoSupport = new UndoManager();
|
final UndoManager undoSupport = new UndoManager();
|
||||||
|
|
||||||
|
@ -138,12 +126,10 @@ public final class TunedUtilities {
|
||||||
return undoSupport;
|
return undoSupport;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static boolean isMaximized(Frame frame) {
|
public static boolean isMaximized(Frame frame) {
|
||||||
return (frame.getExtendedState() & Frame.MAXIMIZED_BOTH) != 0;
|
return (frame.getExtendedState() & Frame.MAXIMIZED_BOTH) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static List<String> showMultiValueInputDialog(final String text, final String initialValue, final String title, final Component parent) throws InvocationTargetException, InterruptedException {
|
public static List<String> showMultiValueInputDialog(final String text, final String initialValue, final String title, final Component parent) throws InvocationTargetException, InterruptedException {
|
||||||
String input = showInputDialog(text, initialValue, title, parent);
|
String input = showInputDialog(text, initialValue, title, parent);
|
||||||
if (input == null || input.isEmpty()) {
|
if (input == null || input.isEmpty()) {
|
||||||
|
@ -168,25 +154,28 @@ public final class TunedUtilities {
|
||||||
return singletonList(input);
|
return singletonList(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static String showInputDialog(final String text, final String initialValue, final String title, final Component parent) throws InvocationTargetException, InterruptedException {
|
public static String showInputDialog(final String text, final String initialValue, final String title, final Component parent) throws InvocationTargetException, InterruptedException {
|
||||||
final StringBuilder buffer = new StringBuilder();
|
final StringBuilder buffer = new StringBuilder();
|
||||||
SwingUtilities.invokeAndWait(new Runnable() {
|
|
||||||
|
Runnable runnable = new Runnable() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
Object value = JOptionPane.showInputDialog(parent, text, title, PLAIN_MESSAGE, null, null, initialValue);
|
Object value = JOptionPane.showInputDialog(parent, text, title, PLAIN_MESSAGE, null, null, initialValue);
|
||||||
|
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
buffer.append(value);
|
buffer.append(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
if (SwingUtilities.isEventDispatchThread()) {
|
||||||
|
runnable.run();
|
||||||
|
} else {
|
||||||
|
SwingUtilities.invokeAndWait(runnable);
|
||||||
|
}
|
||||||
|
|
||||||
return buffer.length() == 0 ? null : buffer.toString();
|
return buffer.length() == 0 ? null : buffer.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static Window getWindow(Object component) {
|
public static Window getWindow(Object component) {
|
||||||
if (component instanceof Window)
|
if (component instanceof Window)
|
||||||
return (Window) component;
|
return (Window) component;
|
||||||
|
@ -197,7 +186,6 @@ public final class TunedUtilities {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static Point getOffsetLocation(Window owner) {
|
public static Point getOffsetLocation(Window owner) {
|
||||||
if (owner == null) {
|
if (owner == null) {
|
||||||
Window[] toplevel = Window.getOwnerlessWindows();
|
Window[] toplevel = Window.getOwnerlessWindows();
|
||||||
|
@ -215,7 +203,6 @@ public final class TunedUtilities {
|
||||||
return new Point(p.x + d.width / 4, p.y + d.height / 7);
|
return new Point(p.x + d.width / 4, p.y + d.height / 7);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static Image getImage(Icon icon) {
|
public static Image getImage(Icon icon) {
|
||||||
if (icon == null)
|
if (icon == null)
|
||||||
return null;
|
return null;
|
||||||
|
@ -233,12 +220,10 @@ public final class TunedUtilities {
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static Dimension getDimension(Icon icon) {
|
public static Dimension getDimension(Icon icon) {
|
||||||
return new Dimension(icon.getIconWidth(), icon.getIconHeight());
|
return new Dimension(icon.getIconWidth(), icon.getIconHeight());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static Timer invokeLater(int delay, final Runnable runnable) {
|
public static Timer invokeLater(int delay, final Runnable runnable) {
|
||||||
Timer timer = new Timer(delay, new ActionListener() {
|
Timer timer = new Timer(delay, new ActionListener() {
|
||||||
|
|
||||||
|
@ -254,7 +239,6 @@ public final class TunedUtilities {
|
||||||
return timer;
|
return timer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When trying to drag a row of a multi-select JTable, it will start selecting rows instead of initiating a drag. This TableUI will give the JTable proper dnd behaviour.
|
* When trying to drag a row of a multi-select JTable, it will start selecting rows instead of initiating a drag. This TableUI will give the JTable proper dnd behaviour.
|
||||||
*/
|
*/
|
||||||
|
@ -265,7 +249,6 @@ public final class TunedUtilities {
|
||||||
return new DragDropRowMouseInputHandler();
|
return new DragDropRowMouseInputHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected class DragDropRowMouseInputHandler extends MouseInputHandler {
|
protected class DragDropRowMouseInputHandler extends MouseInputHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -280,7 +263,6 @@ public final class TunedUtilities {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dummy constructor to prevent instantiation.
|
* Dummy constructor to prevent instantiation.
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in New Issue