From 379f0a9cc19f0de605a96468761c5bb1c59b163d Mon Sep 17 00:00:00 2001 From: Reinhard Pointner Date: Mon, 22 Aug 2011 03:43:22 +0000 Subject: [PATCH] * support manual input of movie/series title (as fallback if auto-detection fails or if forced via SHIFT-clicking the data source) * fixed movie mode issues (osdb title/year parsing problem, nfo file imdbid parser problem) --- .../ui/panel/rename/AutoCompleteMatcher.java | 2 +- .../ui/panel/rename/EpisodeListMatcher.java | 27 +++++++--- .../ui/panel/rename/MovieHashMatcher.java | 49 +++++++++++++------ .../filebot/ui/panel/rename/RenamePanel.java | 7 +-- .../filebot/web/MovieDescriptor.java | 5 -- .../filebot/web/OpenSubtitlesXmlRpc.java | 21 ++++++-- .../sourceforge/tuned/ui/TunedUtilities.java | 10 ++++ .../filebot/web/OpenSubtitlesXmlRpcTest.java | 4 +- 8 files changed, 88 insertions(+), 37 deletions(-) diff --git a/source/net/sourceforge/filebot/ui/panel/rename/AutoCompleteMatcher.java b/source/net/sourceforge/filebot/ui/panel/rename/AutoCompleteMatcher.java index da733b2c..e4dc8893 100644 --- a/source/net/sourceforge/filebot/ui/panel/rename/AutoCompleteMatcher.java +++ b/source/net/sourceforge/filebot/ui/panel/rename/AutoCompleteMatcher.java @@ -11,5 +11,5 @@ import net.sourceforge.filebot.similarity.Match; interface AutoCompleteMatcher { - List> match(List files, Locale locale) throws Exception; + List> match(List files, Locale locale, boolean autodetection) throws Exception; } diff --git a/source/net/sourceforge/filebot/ui/panel/rename/EpisodeListMatcher.java b/source/net/sourceforge/filebot/ui/panel/rename/EpisodeListMatcher.java index b8aff17d..18fe242a 100644 --- a/source/net/sourceforge/filebot/ui/panel/rename/EpisodeListMatcher.java +++ b/source/net/sourceforge/filebot/ui/panel/rename/EpisodeListMatcher.java @@ -2,6 +2,8 @@ package net.sourceforge.filebot.ui.panel.rename; +import static java.util.Collections.*; +import static javax.swing.JOptionPane.*; import static net.sourceforge.filebot.MediaTypes.*; import static net.sourceforge.tuned.FileUtilities.*; import static net.sourceforge.tuned.ui.TunedUtilities.*; @@ -48,12 +50,23 @@ class EpisodeListMatcher implements AutoCompleteMatcher { } - protected Collection detectSeriesNames(Collection files) { - // detect series name(s) from files - Collection names = new SeriesNameMatcher().matchAll(files.toArray(new File[0])); + protected Collection grabSeriesNames(Collection files, boolean autodetect) { + Collection names = null; - if (names.isEmpty()) - throw new IllegalArgumentException("Cannot determine series name."); + // auto-detect series name(s) from files + if (autodetect) { + names = new SeriesNameMatcher().matchAll(files.toArray(new File[0])); + } + + // require user input if auto-detection fails + if (names == null || names.isEmpty()) { + String suggestion = new SeriesNameMatcher().matchBySeasonEpisodePattern(getName(files.iterator().next())); + String input = showInputDialog(null, "Enter series name:", suggestion); + + if (input != null) { + names = singleton(input); + } + } return names; } @@ -159,12 +172,12 @@ class EpisodeListMatcher implements AutoCompleteMatcher { @Override - public List> match(final List files, Locale locale) throws Exception { + public List> match(final List files, Locale locale, boolean autodetection) throws Exception { // focus on movie and subtitle files List mediaFiles = FileUtilities.filter(files, VIDEO_FILES, SUBTITLE_FILES); // detect series name and fetch episode list - Set episodes = fetchEpisodeSet(detectSeriesNames(mediaFiles), locale); + Set episodes = fetchEpisodeSet(grabSeriesNames(mediaFiles, autodetection), locale); List> matches = new ArrayList>(); diff --git a/source/net/sourceforge/filebot/ui/panel/rename/MovieHashMatcher.java b/source/net/sourceforge/filebot/ui/panel/rename/MovieHashMatcher.java index 3b17c496..970a0993 100644 --- a/source/net/sourceforge/filebot/ui/panel/rename/MovieHashMatcher.java +++ b/source/net/sourceforge/filebot/ui/panel/rename/MovieHashMatcher.java @@ -2,11 +2,13 @@ package net.sourceforge.filebot.ui.panel.rename; +import static javax.swing.JOptionPane.*; import static net.sourceforge.filebot.MediaTypes.*; import static net.sourceforge.tuned.FileUtilities.*; import static net.sourceforge.tuned.ui.TunedUtilities.*; import java.io.File; +import java.io.FileInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; @@ -45,21 +47,26 @@ class MovieHashMatcher implements AutoCompleteMatcher { @Override - public List> match(final List files, Locale locale) throws Exception { + public List> match(final List files, Locale locale, boolean autodetect) throws Exception { // handle movie files File[] movieFiles = filter(files, VIDEO_FILES).toArray(new File[0]); - MovieDescriptor[] movieDescriptors = service.getMovieDescriptors(movieFiles, locale); + MovieDescriptor[] movieByFileHash = new MovieDescriptor[movieFiles.length]; + + // match movie hashes online + if (autodetect) { + movieByFileHash = service.getMovieDescriptors(movieFiles, locale); + } // map movies to (possibly multiple) files (in natural order) Map> filesByMovie = new HashMap>(); // map all files by movie for (int i = 0; i < movieFiles.length; i++) { - MovieDescriptor movie = movieDescriptors[i]; + MovieDescriptor movie = movieByFileHash[i]; // unknown hash, try via imdb id from nfo file if (movie == null) { - movie = determineMovie(movieFiles[i], locale); + movie = grabMovieName(movieFiles[i], locale, autodetect); } // check if we managed to lookup the movie descriptor @@ -119,17 +126,17 @@ class MovieHashMatcher implements AutoCompleteMatcher { } - protected Set grepImdbId(File... files) throws IOException { + private Set grepImdbId(File... files) throws IOException { Set collection = new HashSet(); for (File file : files) { - Scanner scanner = new Scanner(file); + Scanner scanner = new Scanner(new FileInputStream(file)); try { // scan for imdb id patterns like tt1234567 String imdb = null; - while ((imdb = scanner.findWithinHorizon("(?<=tt)\\d{7}", 32 * 1024)) != null) { + while ((imdb = scanner.findWithinHorizon("(?<=tt)\\d{7}", 64 * 1024)) != null) { collection.add(Integer.parseInt(imdb)); } } finally { @@ -141,7 +148,12 @@ class MovieHashMatcher implements AutoCompleteMatcher { } - protected MovieDescriptor determineMovie(File movieFile, Locale locale) throws Exception { + private String normalizeMovieName(File movie) { + return getName(movie).replaceAll("\\p{Punct}+", " ").trim(); + } + + + protected MovieDescriptor grabMovieName(File movieFile, Locale locale, boolean autodetect) throws Exception { List options = new ArrayList(); // try to grep imdb id from nfo files @@ -153,15 +165,20 @@ class MovieHashMatcher implements AutoCompleteMatcher { } } - // search by file name - if (options.isEmpty()) { - String query = getName(movieFile).replaceAll("\\p{Punct}+", " ").trim(); - options = service.searchMovie(query, locale); + // search by file name or folder name + for (File it : new File[] { movieFile, movieFile.getParentFile() }) { + if (autodetect && options.isEmpty()) { + options = service.searchMovie(normalizeMovieName(it), locale); + } + } + + // allow manual user input + if (options.isEmpty() || !autodetect) { + String suggestion = options.isEmpty() ? normalizeMovieName(movieFile) : options.get(0).getName(); + String input = showInputDialog(null, "Enter movie name:", suggestion); - // search by folder name - if (options.isEmpty()) { - query = getName(movieFile.getParentFile()).replaceAll("\\p{Punct}+", " ").trim(); - options = service.searchMovie(query, locale); + if (input != null) { + options = service.searchMovie(input, locale); } } diff --git a/source/net/sourceforge/filebot/ui/panel/rename/RenamePanel.java b/source/net/sourceforge/filebot/ui/panel/rename/RenamePanel.java index 1bc4752d..a4e6c952 100644 --- a/source/net/sourceforge/filebot/ui/panel/rename/RenamePanel.java +++ b/source/net/sourceforge/filebot/ui/panel/rename/RenamePanel.java @@ -327,7 +327,7 @@ public class RenamePanel extends JComponent { @Override - public void actionPerformed(ActionEvent evt) { + public void actionPerformed(final ActionEvent evt) { // auto-match in progress namesList.firePropertyChange(LOADING_PROPERTY, false, true); @@ -338,11 +338,12 @@ public class RenamePanel extends JComponent { private final List remainingFiles = new LinkedList(renameModel.files()); private final Locale locale = new Locale(persistentPreferredLanguage.getValue()); + private final boolean autodetection = !isShiftDown(evt); // skip name auto-detection if SHIFT is pressed + - @Override protected List> doInBackground() throws Exception { - List> matches = matcher.match(remainingFiles, locale); + List> matches = matcher.match(remainingFiles, locale, autodetection); // remove matched files for (Match match : matches) { diff --git a/source/net/sourceforge/filebot/web/MovieDescriptor.java b/source/net/sourceforge/filebot/web/MovieDescriptor.java index a8e7cdc6..2382a842 100644 --- a/source/net/sourceforge/filebot/web/MovieDescriptor.java +++ b/source/net/sourceforge/filebot/web/MovieDescriptor.java @@ -11,11 +11,6 @@ public class MovieDescriptor extends SearchResult { private final int imdbId; - public MovieDescriptor(String name, int imdbId) { - this(name, -1, imdbId); - } - - public MovieDescriptor(String name, int year, int imdbId) { super(name); diff --git a/source/net/sourceforge/filebot/web/OpenSubtitlesXmlRpc.java b/source/net/sourceforge/filebot/web/OpenSubtitlesXmlRpc.java index 74e86889..02205f3f 100644 --- a/source/net/sourceforge/filebot/web/OpenSubtitlesXmlRpc.java +++ b/source/net/sourceforge/filebot/web/OpenSubtitlesXmlRpc.java @@ -18,6 +18,10 @@ import java.util.Map; import java.util.NoSuchElementException; import java.util.Scanner; import java.util.Map.Entry; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.zip.DeflaterInputStream; import redstone.xmlrpc.XmlRpcClient; @@ -123,11 +127,22 @@ public class OpenSubtitlesXmlRpc { List> movieData = (List>) response.get("data"); List movies = new ArrayList(); + // title pattern + Pattern pattern = Pattern.compile("(.+)[(](\\d{4})[)]"); + for (Map movie : movieData) { - // get non-aka title (aka titles were separated by Â, and then aka later on) - Scanner titleScanner = new Scanner(movie.get("title")).useDelimiter("(\u00C2)|(\\s+aka\\s+)"); + // match movie name and movie year from search result + Matcher matcher = pattern.matcher(movie.get("title")); - movies.add(new MovieDescriptor(titleScanner.next().trim(), Integer.parseInt(movie.get("id")))); + if (matcher.find()) { + String name = matcher.group(1).trim(); + int year = Integer.parseInt(matcher.group(2)); + int imdbid = Integer.parseInt(movie.get("id")); + + movies.add(new MovieDescriptor(name, year, imdbid)); + } else { + Logger.getLogger(OpenSubtitlesXmlRpc.class.getName()).log(Level.WARNING, "Error parsing title: " + movie); + } } return movies; diff --git a/source/net/sourceforge/tuned/ui/TunedUtilities.java b/source/net/sourceforge/tuned/ui/TunedUtilities.java index bc6a4386..bd81367d 100644 --- a/source/net/sourceforge/tuned/ui/TunedUtilities.java +++ b/source/net/sourceforge/tuned/ui/TunedUtilities.java @@ -49,6 +49,16 @@ public final class TunedUtilities { } + public static boolean isShiftDown(ActionEvent evt) { + return checkModifiers(evt.getModifiers(), ActionEvent.SHIFT_MASK); + } + + + public static boolean checkModifiers(int modifiers, int mask) { + return ((modifiers & mask) == mask); + } + + public static JButton createImageButton(Action action) { JButton button = new JButton(action); button.setHideActionText(true); diff --git a/test/net/sourceforge/filebot/web/OpenSubtitlesXmlRpcTest.java b/test/net/sourceforge/filebot/web/OpenSubtitlesXmlRpcTest.java index 86180e57..eafb537a 100644 --- a/test/net/sourceforge/filebot/web/OpenSubtitlesXmlRpcTest.java +++ b/test/net/sourceforge/filebot/web/OpenSubtitlesXmlRpcTest.java @@ -35,11 +35,11 @@ public class OpenSubtitlesXmlRpcTest { @Test public void search() throws Exception { List list = xmlrpc.searchMoviesOnIMDB("babylon 5"); - MovieDescriptor sample = (MovieDescriptor) list.get(0); // check sample entry - assertEquals("\"Babylon 5\" (1994)", sample.getName()); + assertEquals("\"Babylon 5\"", sample.getName()); + assertEquals(1994, sample.getYear()); assertEquals(105946, sample.getImdbId()); }