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

* added movie identification support in rename panel

This commit is contained in:
Reinhard Pointner 2009-11-21 19:21:46 +00:00
parent f8f1d5b5b3
commit 3c81eb7f5d
9 changed files with 154 additions and 44 deletions

BIN
fw/search.themoviedb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 695 B

View File

@ -0,0 +1,14 @@
package net.sourceforge.filebot.ui.panel.rename;
import java.io.File;
import java.util.List;
import net.sourceforge.filebot.similarity.Match;
interface AutoCompleteMatcher {
List<Match<File, ?>> match(List<File> files) throws Exception;
}

View File

@ -26,7 +26,6 @@ import java.util.concurrent.RunnableFuture;
import javax.swing.Action;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import net.sourceforge.filebot.similarity.Match;
import net.sourceforge.filebot.similarity.Matcher;
@ -40,24 +39,13 @@ import net.sourceforge.filebot.web.SearchResult;
import net.sourceforge.tuned.FileUtilities;
class AutoFetchEpisodeListMatcher extends SwingWorker<List<Match<File, Episode>>, Void> {
class EpisodeListMatcher implements AutoCompleteMatcher {
private final EpisodeListProvider provider;
private final List<File> files;
private final SimilarityMetric[] metrics;
public AutoFetchEpisodeListMatcher(EpisodeListProvider provider, Collection<File> files, SimilarityMetric[] metrics) {
public EpisodeListMatcher(EpisodeListProvider provider) {
this.provider = provider;
this.files = new LinkedList<File>(files);
this.metrics = metrics.clone();
}
public List<File> remainingFiles() {
return Collections.unmodifiableList(files);
}
@ -171,36 +159,30 @@ class AutoFetchEpisodeListMatcher extends SwingWorker<List<Match<File, Episode>>
@Override
protected List<Match<File, Episode>> doInBackground() throws Exception {
public List<Match<File, ?>> match(final List<File> files) throws Exception {
// focus on movie and subtitle files
List<File> mediaFiles = FileUtilities.filter(files, VIDEO_FILES, SUBTITLE_FILES);
// detect series name and fetch episode list
Set<Episode> episodes = fetchEpisodeSet(detectSeriesNames(mediaFiles));
List<Match<File, Episode>> matches = new ArrayList<Match<File, Episode>>();
List<Match<File, ?>> matches = new ArrayList<Match<File, ?>>();
// group by subtitles first and then by files in general
for (List<File> filesPerType : mapByFileType(mediaFiles, VIDEO_FILES, SUBTITLE_FILES).values()) {
Matcher<File, Episode> matcher = new Matcher<File, Episode>(filesPerType, episodes, metrics);
Matcher<File, Episode> matcher = new Matcher<File, Episode>(filesPerType, episodes, MatchSimilarityMetric.defaultSequence());
matches.addAll(matcher.match());
}
// restore original order
Collections.sort(matches, new Comparator<Match<File, Episode>>() {
Collections.sort(matches, new Comparator<Match<File, ?>>() {
@Override
public int compare(Match<File, Episode> o1, Match<File, Episode> o2) {
public int compare(Match<File, ?> o1, Match<File, ?> o2) {
return files.indexOf(o1.getValue()) - files.indexOf(o2.getValue());
}
});
// update remaining files
for (Match<File, Episode> match : matches) {
files.remove(match.getValue());
}
return matches;
}

View File

@ -0,0 +1,29 @@
package net.sourceforge.filebot.ui.panel.rename;
import net.sourceforge.filebot.similarity.Match;
import net.sourceforge.filebot.web.MovieDescriptor;
class MovieFormatter implements MatchFormatter {
@Override
public boolean canFormat(Match<?, ?> match) {
return match.getValue() instanceof MovieDescriptor;
}
@Override
public String preview(Match<?, ?> match) {
return format(match);
}
@Override
public String format(Match<?, ?> match) {
MovieDescriptor movie = (MovieDescriptor) match.getValue();
return movie.getName();
}
}

View File

@ -0,0 +1,44 @@
package net.sourceforge.filebot.ui.panel.rename;
import static net.sourceforge.filebot.MediaTypes.*;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import net.sourceforge.filebot.similarity.Match;
import net.sourceforge.filebot.web.MovieDescriptor;
import net.sourceforge.filebot.web.MovieIdentificationService;
import net.sourceforge.tuned.FileUtilities;
class MovieHashMatcher implements AutoCompleteMatcher {
private final MovieIdentificationService service;
public MovieHashMatcher(MovieIdentificationService service) {
this.service = service;
}
@Override
public List<Match<File, ?>> match(List<File> files) throws Exception {
// focus on movie and subtitle files
File[] movieFiles = FileUtilities.filter(files, VIDEO_FILES).toArray(new File[0]);
MovieDescriptor[] movieDescriptors = service.getMovieDescriptors(movieFiles);
List<Match<File, ?>> matches = new ArrayList<Match<File, ?>>();
for (int i = 0; i < movieDescriptors.length; i++) {
if (movieDescriptors[i] != null) {
matches.add(new Match<File, MovieDescriptor>(movieFiles[i], movieDescriptors[i]));
}
}
return matches;
}
}

View File

@ -12,6 +12,8 @@ import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -24,6 +26,7 @@ import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import ca.odell.glazedlists.ListSelection;
import ca.odell.glazedlists.swing.EventSelectionModel;
@ -37,6 +40,8 @@ import net.sourceforge.filebot.web.AnidbClient;
import net.sourceforge.filebot.web.Episode;
import net.sourceforge.filebot.web.EpisodeListProvider;
import net.sourceforge.filebot.web.IMDbClient;
import net.sourceforge.filebot.web.MovieDescriptor;
import net.sourceforge.filebot.web.TMDbClient;
import net.sourceforge.filebot.web.TVDotComClient;
import net.sourceforge.filebot.web.TVRageClient;
import net.sourceforge.filebot.web.TheTVDBClient;
@ -75,6 +80,9 @@ public class RenamePanel extends JComponent {
// filename formatter
renameModel.useFormatter(File.class, new FileNameFormatter(renameModel.preserveExtension()));
// movie formatter
renameModel.useFormatter(MovieDescriptor.class, new MovieFormatter());
try {
// restore custom episode formatter
renameModel.useFormatter(Episode.class, new EpisodeExpressionFormatter(persistentFormatExpression.getValue()));
@ -135,12 +143,17 @@ public class RenamePanel extends JComponent {
protected ActionPopup createFetchPopup() {
final ActionPopup actionPopup = new ActionPopup("Fetch Episode List", ResourceManager.getIcon("action.fetch"));
// create actions for match popup
actionPopup.add(new AutoFetchEpisodeListAction(new TVRageClient()));
actionPopup.add(new AutoFetchEpisodeListAction(new AnidbClient()));
actionPopup.add(new AutoFetchEpisodeListAction(new TVDotComClient()));
actionPopup.add(new AutoFetchEpisodeListAction(new IMDbClient()));
actionPopup.add(new AutoFetchEpisodeListAction(new TheTVDBClient(getApplicationProperty("thetvdb.apikey"))));
// create actions for match popup episode list completion
for (EpisodeListProvider provider : Arrays.asList(new TVRageClient(), new AnidbClient(), new TVDotComClient(), new IMDbClient(), new TheTVDBClient(getApplicationProperty("thetvdb.apikey")))) {
actionPopup.add(new AutoCompleteAction(provider.getName(), provider.getIcon(), new EpisodeListMatcher(provider)));
}
actionPopup.addSeparator();
actionPopup.addDescription(new JLabel("Movie Identification:"));
// create action for movie name completion
TMDbClient tmdb = new TMDbClient(getApplicationProperty("themoviedb.apikey"));
actionPopup.add(new AutoCompleteAction(tmdb.getName(), tmdb.getIcon(), new MovieHashMatcher(tmdb)));
actionPopup.addSeparator();
actionPopup.addDescription(new JLabel("Options:"));
@ -247,15 +260,15 @@ public class RenamePanel extends JComponent {
}
protected class AutoFetchEpisodeListAction extends AbstractAction {
protected class AutoCompleteAction extends AbstractAction {
private final EpisodeListProvider provider;
private final AutoCompleteMatcher matcher;
public AutoFetchEpisodeListAction(EpisodeListProvider provider) {
super(provider.getName(), provider.getIcon());
public AutoCompleteAction(String name, Icon icon, AutoCompleteMatcher matcher) {
super(name, icon);
this.provider = provider;
this.matcher = matcher;
// disable action while episode list matcher is working
namesList.addPropertyChangeListener(LOADING_PROPERTY, new PropertyChangeListener() {
@ -277,23 +290,38 @@ public class RenamePanel extends JComponent {
// clear names list
renameModel.values().clear();
AutoFetchEpisodeListMatcher worker = new AutoFetchEpisodeListMatcher(provider, renameModel.files(), MatchSimilarityMetric.defaultSequence()) {
SwingWorker<List<Match<File, ?>>, Void> worker = new SwingWorker<List<Match<File, ?>>, Void>() {
private final List<File> remainingFiles = new LinkedList<File>(renameModel.files());
@Override
protected List<Match<File, ?>> doInBackground() throws Exception {
List<Match<File, ?>> matches = matcher.match(remainingFiles);
// remove matched files
for (Match<File, ?> match : matches) {
remainingFiles.remove(match.getValue());
}
return matches;
}
@Override
protected void done() {
try {
List<Match<Object, File>> matches = new ArrayList<Match<Object, File>>();
for (Match<File, Episode> match : get()) {
for (Match<File, ?> match : get()) {
matches.add(new Match<Object, File>(match.getCandidate(), match.getValue()));
}
renameModel.clear();
renameModel.addAll(matches);
// add remaining file entries
renameModel.files().addAll(remainingFiles());
renameModel.files().addAll(remainingFiles);
} catch (Exception e) {
Logger.getLogger("ui").log(Level.WARNING, ExceptionUtilities.getRootCauseMessage(e), e);
} finally {

View File

@ -136,11 +136,10 @@ public class AnidbClient implements EpisodeListProvider {
List<Episode> episodes = new ArrayList<Episode>(25);
for (Node node : selectNodes("//ep", dom)) {
String flags = getTextContent("flags", node);
String number = getTextContent("epno", node);
// allow only normal and recap episodes
if (flags == null || flags.equals("2")) {
String number = getTextContent("epno", node);
// ignore special episodes
if (number != null && number.matches("\\d+")) {
String title = selectString(".//title[@lang='en']", node);
// no seasons for anime

View File

@ -13,9 +13,13 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import javax.swing.Icon;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
import net.sourceforge.filebot.ResourceManager;
public class TMDbClient implements MovieIdentificationService {
@ -30,6 +34,16 @@ public class TMDbClient implements MovieIdentificationService {
}
public String getName() {
return "TheMovieDB";
}
public Icon getIcon() {
return ResourceManager.getIcon("search.themoviedb");
}
public List<MovieDescriptor> searchMovie(String query) throws IOException, SAXException {
return getMovies("Movie.search", query);
}