* 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)
This commit is contained in:
Reinhard Pointner 2011-08-22 03:43:22 +00:00
parent 8ab04ba075
commit 379f0a9cc1
8 changed files with 88 additions and 37 deletions

View File

@ -11,5 +11,5 @@ import net.sourceforge.filebot.similarity.Match;
interface AutoCompleteMatcher {
List<Match<File, ?>> match(List<File> files, Locale locale) throws Exception;
List<Match<File, ?>> match(List<File> files, Locale locale, boolean autodetection) throws Exception;
}

View File

@ -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<String> detectSeriesNames(Collection<File> files) {
// detect series name(s) from files
Collection<String> names = new SeriesNameMatcher().matchAll(files.toArray(new File[0]));
protected Collection<String> grabSeriesNames(Collection<File> files, boolean autodetect) {
Collection<String> 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<File, ?>> match(final List<File> files, Locale locale) throws Exception {
public List<Match<File, ?>> match(final List<File> files, Locale locale, boolean autodetection) 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), locale);
Set<Episode> episodes = fetchEpisodeSet(grabSeriesNames(mediaFiles, autodetection), locale);
List<Match<File, ?>> matches = new ArrayList<Match<File, ?>>();

View File

@ -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<File, ?>> match(final List<File> files, Locale locale) throws Exception {
public List<Match<File, ?>> match(final List<File> 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<MovieDescriptor, SortedSet<File>> filesByMovie = new HashMap<MovieDescriptor, SortedSet<File>>();
// 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<Integer> grepImdbId(File... files) throws IOException {
private Set<Integer> grepImdbId(File... files) throws IOException {
Set<Integer> collection = new HashSet<Integer>();
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<MovieDescriptor> options = new ArrayList<MovieDescriptor>();
// 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);
}
}

View File

@ -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<File> remainingFiles = new LinkedList<File>(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<Match<File, ?>> doInBackground() throws Exception {
List<Match<File, ?>> matches = matcher.match(remainingFiles, locale);
List<Match<File, ?>> matches = matcher.match(remainingFiles, locale, autodetection);
// remove matched files
for (Match<File, ?> match : matches) {

View File

@ -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);

View File

@ -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<Map<String, String>> movieData = (List<Map<String, String>>) response.get("data");
List<MovieDescriptor> movies = new ArrayList<MovieDescriptor>();
// title pattern
Pattern pattern = Pattern.compile("(.+)[(](\\d{4})[)]");
for (Map<String, String> 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;

View File

@ -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);

View File

@ -35,11 +35,11 @@ public class OpenSubtitlesXmlRpcTest {
@Test
public void search() throws Exception {
List<MovieDescriptor> 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());
}