+ support Movie disk folders in movie mode
+ improved handling for derivate files (files with the same name but different extensions) in movie mode
This commit is contained in:
parent
67fe97c345
commit
cc5845b2a0
|
@ -109,8 +109,8 @@
|
|||
<include name="org/slf4j/**" />
|
||||
</zipfileset>
|
||||
|
||||
<zipfileset src="${dir.lib}/guava.jar">
|
||||
<include name="com/google/common/**" />
|
||||
<zipfileset src="${dir.lib}/commons-io.jar">
|
||||
<include name="org/apache/commons/io/**" />
|
||||
</zipfileset>
|
||||
|
||||
<zipfileset src="${dir.lib}/jna.jar">
|
||||
|
|
|
@ -49,7 +49,7 @@
|
|||
<jar href="mediainfo.jar" download="lazy" part="native" />
|
||||
<jar href="nekohtml.jar" download="lazy" part="scraper" />
|
||||
<jar href="xercesImpl.jar" download="lazy" part="scraper" />
|
||||
<jar href="guava.jar" download="lazy" />
|
||||
<jar href="commons-io.jar" download="lazy" />
|
||||
<jar href="junrar-custom.jar" download="lazy" />
|
||||
</resources>
|
||||
|
||||
|
|
Binary file not shown.
BIN
lib/guava.jar
BIN
lib/guava.jar
Binary file not shown.
|
@ -3,7 +3,6 @@ package net.sourceforge.filebot.cli;
|
|||
|
||||
|
||||
import static java.lang.String.*;
|
||||
import static java.util.Arrays.*;
|
||||
import static java.util.Collections.*;
|
||||
import static net.sourceforge.filebot.MediaTypes.*;
|
||||
import static net.sourceforge.filebot.WebServices.*;
|
||||
|
@ -47,6 +46,7 @@ import net.sourceforge.filebot.format.MediaBindingBean;
|
|||
import net.sourceforge.filebot.hash.HashType;
|
||||
import net.sourceforge.filebot.hash.VerificationFileReader;
|
||||
import net.sourceforge.filebot.hash.VerificationFileWriter;
|
||||
import net.sourceforge.filebot.media.ReleaseInfo;
|
||||
import net.sourceforge.filebot.similarity.EpisodeMetrics;
|
||||
import net.sourceforge.filebot.similarity.Match;
|
||||
import net.sourceforge.filebot.similarity.Matcher;
|
||||
|
@ -260,49 +260,68 @@ public class CmdlineOperations implements CmdlineInterface {
|
|||
}
|
||||
|
||||
|
||||
public List<File> renameMovie(Collection<File> mediaFiles, String query, ExpressionFormat format, MovieIdentificationService service, Locale locale, boolean strict) throws Exception {
|
||||
public List<File> renameMovie(Collection<File> files, String query, ExpressionFormat format, MovieIdentificationService service, Locale locale, boolean strict) throws Exception {
|
||||
CLILogger.config(format("Rename movies using [%s]", service.getName()));
|
||||
|
||||
// handle movie files
|
||||
File[] movieFiles = filter(mediaFiles, VIDEO_FILES).toArray(new File[0]);
|
||||
File[] subtitleFiles = filter(mediaFiles, SUBTITLE_FILES).toArray(new File[0]);
|
||||
Movie[] movieByFileHash = new Movie[movieFiles.length];
|
||||
List<File> movieFiles = filter(files, VIDEO_FILES);
|
||||
|
||||
Map<File, List<File>> derivatesByMovieFile = new HashMap<File, List<File>>();
|
||||
for (File movieFile : movieFiles) {
|
||||
derivatesByMovieFile.put(movieFile, new ArrayList<File>());
|
||||
}
|
||||
for (File file : files) {
|
||||
for (File movieFile : movieFiles) {
|
||||
if (!file.equals(movieFile) && isDerived(file, movieFile)) {
|
||||
derivatesByMovieFile.get(movieFile).add(file);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<File> standaloneFiles = new ArrayList<File>(files);
|
||||
for (List<File> derivates : derivatesByMovieFile.values()) {
|
||||
standaloneFiles.removeAll(derivates);
|
||||
}
|
||||
|
||||
List<File> movieMatchFiles = new ArrayList<File>();
|
||||
movieMatchFiles.addAll(movieFiles);
|
||||
movieMatchFiles.addAll(filter(files, new ReleaseInfo().getDiskFolderFilter()));
|
||||
movieMatchFiles.addAll(filter(standaloneFiles, SUBTITLE_FILES));
|
||||
|
||||
// map movies to (possibly multiple) files (in natural order)
|
||||
Map<Movie, SortedSet<File>> filesByMovie = new HashMap<Movie, SortedSet<File>>();
|
||||
|
||||
if (movieFiles.length > 0 && query == null) {
|
||||
// match movie hashes online
|
||||
Map<File, Movie> movieByFile = new HashMap<File, Movie>();
|
||||
if (query == null && movieFiles.size() > 0) {
|
||||
try {
|
||||
CLILogger.fine(format("Looking up movie by filehash via [%s]", service.getName()));
|
||||
movieByFileHash = service.getMovieDescriptors(movieFiles, locale);
|
||||
Analytics.trackEvent(service.getName(), "HashLookup", "Movie", movieByFileHash.length - frequency(asList(movieByFileHash), null)); // number of positive hash lookups
|
||||
Map<File, Movie> hashLookup = service.getMovieDescriptors(movieFiles, locale);
|
||||
movieByFile.putAll(hashLookup);
|
||||
Analytics.trackEvent(service.getName(), "HashLookup", "Movie", hashLookup.size()); // number of positive hash lookups
|
||||
} catch (UnsupportedOperationException e) {
|
||||
CLILogger.fine(format("%s: Hash lookup not supported", service.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
if (subtitleFiles.length > 0 && movieFiles.length == 0) {
|
||||
// special handling if there is only subtitle files
|
||||
movieByFileHash = new Movie[subtitleFiles.length];
|
||||
movieFiles = subtitleFiles;
|
||||
subtitleFiles = new File[0];
|
||||
}
|
||||
|
||||
if (query != null) {
|
||||
CLILogger.fine(format("Looking up movie by query [%s]", query));
|
||||
Movie result = (Movie) selectSearchResult(query, service.searchMovie(query, locale), strict).get(0);
|
||||
fill(movieByFileHash, result);
|
||||
// force all mappings
|
||||
for (File file : movieMatchFiles) {
|
||||
movieByFile.put(file, result);
|
||||
}
|
||||
}
|
||||
|
||||
// map movies to (possibly multiple) files (in natural order)
|
||||
Map<Movie, SortedSet<File>> filesByMovie = new HashMap<Movie, SortedSet<File>>();
|
||||
|
||||
// map all files by movie
|
||||
for (int i = 0; i < movieFiles.length; i++) {
|
||||
Movie movie = movieByFileHash[i];
|
||||
for (File file : movieMatchFiles) {
|
||||
Movie movie = movieByFile.get(file);
|
||||
|
||||
// unknown hash, try via imdb id from nfo file
|
||||
if (movie == null) {
|
||||
CLILogger.fine(format("Auto-detect movie from context: [%s]", movieFiles[i]));
|
||||
Collection<Movie> results = detectMovie(movieFiles[i], null, service, locale, strict);
|
||||
CLILogger.fine(format("Auto-detect movie from context: [%s]", file));
|
||||
Collection<Movie> results = detectMovie(file, null, service, locale, strict);
|
||||
movie = (Movie) selectSearchResult(query, results, strict).get(0);
|
||||
|
||||
if (movie != null) {
|
||||
|
@ -320,7 +339,7 @@ public class CmdlineOperations implements CmdlineInterface {
|
|||
filesByMovie.put(movie, movieParts);
|
||||
}
|
||||
|
||||
movieParts.add(movieFiles[i]);
|
||||
movieParts.add(file);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -341,17 +360,13 @@ public class CmdlineOperations implements CmdlineInterface {
|
|||
}
|
||||
|
||||
matches.add(new Match<File, Movie>(file, part));
|
||||
}
|
||||
}
|
||||
|
||||
// handle subtitle files
|
||||
for (File subtitle : subtitleFiles) {
|
||||
// check if subtitle corresponds to a movie file (same name, different extension)
|
||||
for (Match<File, ?> movieMatch : matches) {
|
||||
if (isDerived(subtitle, movieMatch.getValue())) {
|
||||
matches.add(new Match<File, Object>(subtitle, movieMatch.getCandidate()));
|
||||
// movie match found, we're done
|
||||
break;
|
||||
// automatically add matches for derivates
|
||||
List<File> derivates = derivatesByMovieFile.get(file);
|
||||
if (derivates != null) {
|
||||
for (File derivate : derivates) {
|
||||
matches.add(new Match<File, Movie>(derivate, part));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -387,7 +402,7 @@ public class CmdlineOperations implements CmdlineInterface {
|
|||
for (Entry<File, File> it : renameMap.entrySet()) {
|
||||
try {
|
||||
// rename file, throw exception on failure
|
||||
File destination = renameFile(it.getKey(), it.getValue());
|
||||
File destination = moveRename(it.getKey(), it.getValue());
|
||||
CLILogger.info(format("Renamed [%s] to [%s]", it.getKey(), it.getValue()));
|
||||
|
||||
// remember successfully renamed matches for history entry and possible revert
|
||||
|
|
|
@ -37,7 +37,7 @@ File.metaClass.hasExtension = { String... ext -> hasExtension(delegate, ext) }
|
|||
File.metaClass.isDerived = { f -> isDerived(delegate, f) }
|
||||
File.metaClass.validateFileName = { validateFileName(delegate) }
|
||||
File.metaClass.validateFilePath = { validateFilePath(delegate) }
|
||||
File.metaClass.moveTo = { f -> renameFile(delegate, f) }
|
||||
File.metaClass.moveTo = { f -> moveRename(delegate, f instanceof File ? f : new File(f.toString())) }
|
||||
List.metaClass.mapByFolder = { mapByFolder(delegate) }
|
||||
List.metaClass.mapByExtension = { mapByExtension(delegate) }
|
||||
String.metaClass.getExtension = { getExtension(delegate) }
|
||||
|
|
|
@ -8,6 +8,7 @@ import static net.sourceforge.filebot.similarity.Normalization.*;
|
|||
import static net.sourceforge.tuned.FileUtilities.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
@ -49,6 +50,11 @@ public class MediaDetection {
|
|||
private static final ReleaseInfo releaseInfo = new ReleaseInfo();
|
||||
|
||||
|
||||
public static boolean isDiskFolder(File folder) {
|
||||
return releaseInfo.getDiskFolderFilter().accept(folder);
|
||||
}
|
||||
|
||||
|
||||
public static Map<Set<File>, Set<String>> mapSeriesNamesByFiles(Collection<File> files, Locale locale) throws Exception {
|
||||
SortedMap<File, List<File>> filesByFolder = mapByFolder(filter(files, VIDEO_FILES, SUBTITLE_FILES));
|
||||
|
||||
|
@ -156,8 +162,8 @@ public class MediaDetection {
|
|||
Set<Movie> options = new LinkedHashSet<Movie>();
|
||||
|
||||
// lookup by file hash
|
||||
if (hashLookupService != null) {
|
||||
for (Movie movie : hashLookupService.getMovieDescriptors(new File[] { movieFile }, locale)) {
|
||||
if (hashLookupService != null && movieFile.isFile()) {
|
||||
for (Movie movie : hashLookupService.getMovieDescriptors(singleton(movieFile), locale).values()) {
|
||||
if (movie != null) {
|
||||
options.add(movie);
|
||||
}
|
||||
|
|
|
@ -2,13 +2,13 @@
|
|||
package net.sourceforge.filebot.media;
|
||||
|
||||
|
||||
import static java.util.Arrays.*;
|
||||
import static java.util.ResourceBundle.*;
|
||||
import static java.util.regex.Pattern.*;
|
||||
import static net.sourceforge.filebot.similarity.Normalization.*;
|
||||
import static net.sourceforge.tuned.StringUtilities.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
|
@ -125,7 +125,7 @@ public class ReleaseInfo {
|
|||
languageMap.put(locale.getISO3Language(), locale);
|
||||
|
||||
// map display language names for given locales
|
||||
for (Locale language : asList(supportedLanguageName)) {
|
||||
for (Locale language : supportedLanguageName) {
|
||||
languageMap.put(locale.getDisplayLanguage(language), locale);
|
||||
}
|
||||
}
|
||||
|
@ -174,6 +174,11 @@ public class ReleaseInfo {
|
|||
}
|
||||
|
||||
|
||||
public FileFilter getDiskFolderFilter() {
|
||||
return new FolderEntryFilter(compile(getBundle(getClass().getName()).getString("pattern.diskfolder.entry")));
|
||||
}
|
||||
|
||||
|
||||
// fetch release group names online and try to update the data every other day
|
||||
protected final CachedResource<String[]> releaseGroupResource = new PatternResource(getBundle(getClass().getName()).getString("url.release-groups"));
|
||||
protected final CachedResource<String[]> queryBlacklistResource = new PatternResource(getBundle(getClass().getName()).getString("url.query-blacklist"));
|
||||
|
@ -217,4 +222,28 @@ public class ReleaseInfo {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
protected static class FolderEntryFilter implements FileFilter {
|
||||
|
||||
private final Pattern entryPattern;
|
||||
|
||||
|
||||
public FolderEntryFilter(Pattern entryPattern) {
|
||||
this.entryPattern = entryPattern;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean accept(File dir) {
|
||||
if (dir.isDirectory()) {
|
||||
for (String entry : dir.list()) {
|
||||
if (entryPattern.matcher(entry).matches()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -12,3 +12,6 @@ url.query-blacklist: http://filebot.sourceforge.net/data/query-blacklist.txt
|
|||
|
||||
# list of all movies (id, name, year)
|
||||
url.movie-list: http://filebot.sourceforge.net/data/movies.txt.gz
|
||||
|
||||
# disk folder matcher
|
||||
pattern.diskfolder.entry: ^BDMV$|^HVDVD_TS$|^VIDEO_TS$|^AUDIO_TS$|^VCD$
|
||||
|
|
|
@ -2,11 +2,14 @@
|
|||
package net.sourceforge.filebot.ui.rename;
|
||||
|
||||
|
||||
import static net.sourceforge.tuned.FileUtilities.*;
|
||||
import static java.util.Arrays.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import net.sourceforge.filebot.media.MediaDetection;
|
||||
import net.sourceforge.filebot.ui.transfer.FileTransferablePolicy;
|
||||
import net.sourceforge.tuned.FastFile;
|
||||
|
||||
|
@ -35,7 +38,23 @@ class FilesListTransferablePolicy extends FileTransferablePolicy {
|
|||
|
||||
@Override
|
||||
protected void load(List<File> files) {
|
||||
model.addAll(FastFile.foreach(flatten(files, 5, false)));
|
||||
List<File> entries = new ArrayList<File>();
|
||||
LinkedList<File> queue = new LinkedList<File>(files);
|
||||
|
||||
while (queue.size() > 0) {
|
||||
File f = queue.removeFirst();
|
||||
|
||||
if (f.isHidden())
|
||||
continue;
|
||||
|
||||
if (f.isFile() || MediaDetection.isDiskFolder(f)) {
|
||||
entries.add(f);
|
||||
} else {
|
||||
queue.addAll(0, asList(f.listFiles()));
|
||||
}
|
||||
}
|
||||
|
||||
model.addAll(FastFile.foreach(entries));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -27,8 +27,8 @@ import java.util.EnumSet;
|
|||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Pattern;
|
||||
|
@ -50,8 +50,8 @@ import javax.swing.JTable;
|
|||
import javax.swing.JTextField;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import javax.swing.RowFilter;
|
||||
import javax.swing.SortOrder;
|
||||
import javax.swing.RowSorter.SortKey;
|
||||
import javax.swing.SortOrder;
|
||||
import javax.swing.border.CompoundBorder;
|
||||
import javax.swing.border.TitledBorder;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
|
@ -507,7 +507,7 @@ class HistoryDialog extends JDialog {
|
|||
|
||||
for (Entry<File, File> entry : getRenameMap(directory).entrySet()) {
|
||||
try {
|
||||
renameFile(entry.getKey(), entry.getValue());
|
||||
moveRename(entry.getKey(), entry.getValue());
|
||||
count++;
|
||||
} catch (Exception e) {
|
||||
Logger.getLogger(HistoryDialog.class.getName()).log(Level.SEVERE, e.getMessage(), e);
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
package net.sourceforge.filebot.ui.rename;
|
||||
|
||||
|
||||
import static java.util.Arrays.*;
|
||||
import static java.util.Collections.*;
|
||||
import static net.sourceforge.filebot.MediaTypes.*;
|
||||
import static net.sourceforge.filebot.media.MediaDetection.*;
|
||||
import static net.sourceforge.tuned.FileUtilities.*;
|
||||
|
@ -37,6 +35,7 @@ import javax.swing.Action;
|
|||
import javax.swing.SwingUtilities;
|
||||
|
||||
import net.sourceforge.filebot.Analytics;
|
||||
import net.sourceforge.filebot.media.ReleaseInfo;
|
||||
import net.sourceforge.filebot.similarity.Match;
|
||||
import net.sourceforge.filebot.similarity.NameSimilarityMetric;
|
||||
import net.sourceforge.filebot.similarity.SimilarityMetric;
|
||||
|
@ -59,24 +58,30 @@ class MovieHashMatcher implements AutoCompleteMatcher {
|
|||
@Override
|
||||
public List<Match<File, ?>> match(final List<File> files, final Locale locale, final boolean autodetect, final Component parent) throws Exception {
|
||||
// handle movie files
|
||||
File[] movieFiles = filter(files, VIDEO_FILES).toArray(new File[0]);
|
||||
File[] subtitleFiles = filter(files, SUBTITLE_FILES).toArray(new File[0]);
|
||||
Movie[] movieByFileHash = null;
|
||||
List<File> movieFiles = filter(files, VIDEO_FILES);
|
||||
|
||||
if (movieFiles.length > 0) {
|
||||
// match movie hashes online
|
||||
try {
|
||||
movieByFileHash = service.getMovieDescriptors(movieFiles, locale);
|
||||
Analytics.trackEvent(service.getName(), "HashLookup", "Movie", movieByFileHash.length - frequency(asList(movieByFileHash), null)); // number of positive hash lookups
|
||||
} catch (UnsupportedOperationException e) {
|
||||
movieByFileHash = new Movie[movieFiles.length];
|
||||
Map<File, List<File>> derivatesByMovieFile = new HashMap<File, List<File>>();
|
||||
for (File movieFile : movieFiles) {
|
||||
derivatesByMovieFile.put(movieFile, new ArrayList<File>());
|
||||
}
|
||||
} else if (subtitleFiles.length > 0) {
|
||||
// special handling if there is only subtitle files
|
||||
movieByFileHash = new Movie[subtitleFiles.length];
|
||||
movieFiles = subtitleFiles;
|
||||
subtitleFiles = new File[0];
|
||||
for (File file : files) {
|
||||
for (File movieFile : movieFiles) {
|
||||
if (!file.equals(movieFile) && isDerived(file, movieFile)) {
|
||||
derivatesByMovieFile.get(movieFile).add(file);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<File> standaloneFiles = new ArrayList<File>(files);
|
||||
for (List<File> derivates : derivatesByMovieFile.values()) {
|
||||
standaloneFiles.removeAll(derivates);
|
||||
}
|
||||
|
||||
List<File> movieMatchFiles = new ArrayList<File>();
|
||||
movieMatchFiles.addAll(movieFiles);
|
||||
movieMatchFiles.addAll(filter(files, new ReleaseInfo().getDiskFolderFilter()));
|
||||
movieMatchFiles.addAll(filter(standaloneFiles, SUBTITLE_FILES));
|
||||
|
||||
// map movies to (possibly multiple) files (in natural order)
|
||||
Map<Movie, SortedSet<File>> filesByMovie = new HashMap<Movie, SortedSet<File>>();
|
||||
|
@ -84,10 +89,21 @@ class MovieHashMatcher implements AutoCompleteMatcher {
|
|||
// match remaining movies file by file in parallel
|
||||
List<Callable<Entry<File, Movie>>> grabMovieJobs = new ArrayList<Callable<Entry<File, Movie>>>();
|
||||
|
||||
// match movie hashes online
|
||||
Map<File, Movie> movieByFile = new HashMap<File, Movie>();
|
||||
if (movieFiles.size() > 0) {
|
||||
try {
|
||||
Map<File, Movie> hashLookup = service.getMovieDescriptors(movieFiles, locale);
|
||||
movieByFile.putAll(hashLookup);
|
||||
Analytics.trackEvent(service.getName(), "HashLookup", "Movie", hashLookup.size()); // number of positive hash lookups
|
||||
} catch (UnsupportedOperationException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
// map all files by movie
|
||||
for (int i = 0; i < movieFiles.length; i++) {
|
||||
final Movie movie = movieByFileHash[i];
|
||||
final File file = movieFiles[i];
|
||||
for (final File file : movieMatchFiles) {
|
||||
final Movie movie = movieByFile.get(file);
|
||||
grabMovieJobs.add(new Callable<Entry<File, Movie>>() {
|
||||
|
||||
@Override
|
||||
|
@ -146,17 +162,13 @@ class MovieHashMatcher implements AutoCompleteMatcher {
|
|||
}
|
||||
|
||||
matches.add(new Match<File, Movie>(file, part));
|
||||
}
|
||||
}
|
||||
|
||||
// handle subtitle files
|
||||
for (File subtitle : subtitleFiles) {
|
||||
// check if subtitle corresponds to a movie file (same name, different extension)
|
||||
for (Match<File, ?> movieMatch : matches) {
|
||||
if (isDerived(subtitle, movieMatch.getValue())) {
|
||||
matches.add(new Match<File, Object>(subtitle, movieMatch.getCandidate()));
|
||||
// movie match found, we're done
|
||||
break;
|
||||
// automatically add matches for derivates
|
||||
List<File> derivates = derivatesByMovieFile.get(file);
|
||||
if (derivates != null) {
|
||||
for (File derivate : derivates) {
|
||||
matches.add(new Match<File, Movie>(derivate, part));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,16 +13,16 @@ import java.awt.event.ActionEvent;
|
|||
import java.beans.PropertyChangeEvent;
|
||||
import java.io.File;
|
||||
import java.util.AbstractList;
|
||||
import java.util.AbstractMap.SimpleEntry;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.AbstractMap.SimpleEntry;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.logging.Level;
|
||||
|
@ -34,8 +34,8 @@ import javax.swing.SwingWorker;
|
|||
import net.sourceforge.filebot.Analytics;
|
||||
import net.sourceforge.filebot.ResourceManager;
|
||||
import net.sourceforge.tuned.ui.ProgressDialog;
|
||||
import net.sourceforge.tuned.ui.SwingWorkerPropertyChangeAdapter;
|
||||
import net.sourceforge.tuned.ui.ProgressDialog.Cancellable;
|
||||
import net.sourceforge.tuned.ui.SwingWorkerPropertyChangeAdapter;
|
||||
|
||||
|
||||
class RenameAction extends AbstractAction {
|
||||
|
@ -209,7 +209,7 @@ class RenameAction extends AbstractAction {
|
|||
firePropertyChange("currentFile", mapping.getKey(), mapping.getValue());
|
||||
|
||||
// rename file, throw exception on failure
|
||||
renameFile(mapping.getKey(), mapping.getValue());
|
||||
moveRename(mapping.getKey(), mapping.getValue());
|
||||
|
||||
// remember successfully renamed matches for history entry and possible revert
|
||||
renameLog.put(mapping.getKey(), mapping.getValue());
|
||||
|
|
|
@ -10,8 +10,10 @@ import java.io.IOException;
|
|||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Scanner;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
@ -122,7 +124,7 @@ public class IMDbClient implements MovieIdentificationService {
|
|||
|
||||
|
||||
@Override
|
||||
public Movie[] getMovieDescriptors(File[] movieFiles, Locale locale) throws Exception {
|
||||
public Map<File, Movie> getMovieDescriptors(Collection<File> movieFiles, Locale locale) throws Exception {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
|
|
@ -3,8 +3,10 @@ package net.sourceforge.filebot.web;
|
|||
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.swing.Icon;
|
||||
|
||||
|
@ -23,6 +25,6 @@ public interface MovieIdentificationService {
|
|||
public Movie getMovieDescriptor(int imdbid, Locale locale) throws Exception;
|
||||
|
||||
|
||||
public Movie[] getMovieDescriptors(File[] movieFiles, Locale locale) throws Exception;
|
||||
public Map<File, Movie> getMovieDescriptors(Collection<File> movieFiles, Locale locale) throws Exception;
|
||||
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ package net.sourceforge.filebot.web;
|
|||
|
||||
import static java.lang.Math.*;
|
||||
import static java.util.Arrays.*;
|
||||
import static java.util.Collections.*;
|
||||
import static net.sourceforge.filebot.web.OpenSubtitlesHasher.*;
|
||||
|
||||
import java.io.File;
|
||||
|
@ -12,6 +13,7 @@ import java.net.URI;
|
|||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
@ -184,37 +186,36 @@ public class OpenSubtitlesClient implements SubtitleProvider, VideoHashSubtitleS
|
|||
|
||||
|
||||
public Movie getMovieDescriptor(File movieFile, Locale locale) throws Exception {
|
||||
return getMovieDescriptors(new File[] { movieFile }, locale)[0];
|
||||
return getMovieDescriptors(singleton(movieFile), locale).get(movieFile);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Movie[] getMovieDescriptors(File[] movieFiles, Locale locale) throws Exception {
|
||||
public Map<File, Movie> getMovieDescriptors(Collection<File> movieFiles, Locale locale) throws Exception {
|
||||
// create result array
|
||||
Movie[] result = new Movie[movieFiles.length];
|
||||
Map<File, Movie> result = new HashMap<File, Movie>();
|
||||
|
||||
// compute movie hashes
|
||||
Map<String, Integer> indexMap = new HashMap<String, Integer>(movieFiles.length);
|
||||
Map<String, File> hashMap = new HashMap<String, File>(movieFiles.size());
|
||||
|
||||
for (int i = 0; i < movieFiles.length; i++) {
|
||||
if (movieFiles[i].length() > HASH_CHUNK_SIZE) {
|
||||
indexMap.put(computeHash(movieFiles[i]), i); // remember original index
|
||||
for (File file : movieFiles) {
|
||||
if (file.length() > HASH_CHUNK_SIZE) {
|
||||
hashMap.put(computeHash(file), file); // map file by hash
|
||||
}
|
||||
}
|
||||
|
||||
if (indexMap.size() > 0) {
|
||||
if (hashMap.size() > 0) {
|
||||
// require login
|
||||
login();
|
||||
|
||||
// dispatch query for all hashes
|
||||
List<String> hashes = new ArrayList<String>(indexMap.keySet());
|
||||
List<String> hashes = new ArrayList<String>(hashMap.keySet());
|
||||
int batchSize = 50;
|
||||
for (int bn = 0; bn < ceil((float) hashes.size() / batchSize); bn++) {
|
||||
List<String> batch = hashes.subList(bn * batchSize, min((bn * batchSize) + batchSize, hashes.size()));
|
||||
|
||||
for (Entry<String, Movie> entry : xmlrpc.checkMovieHash(batch).entrySet()) {
|
||||
int index = indexMap.get(entry.getKey());
|
||||
result[index] = entry.getValue();
|
||||
result.put(hashMap.get(entry.getKey()), entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import java.io.Serializable;
|
|||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
@ -70,11 +71,6 @@ public class TMDbClient implements MovieIdentificationService {
|
|||
}
|
||||
|
||||
|
||||
public List<Movie> searchMovie(File file, Locale locale) throws IOException, SAXException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
||||
public List<Movie> searchMovie(String hash, long bytesize, Locale locale) throws IOException, SAXException {
|
||||
return getMovies("Media.getInfo", hash + "/" + bytesize, locale);
|
||||
}
|
||||
|
@ -96,17 +92,8 @@ public class TMDbClient implements MovieIdentificationService {
|
|||
|
||||
|
||||
@Override
|
||||
public Movie[] getMovieDescriptors(File[] movieFiles, Locale locale) throws Exception {
|
||||
Movie[] movies = new Movie[movieFiles.length];
|
||||
|
||||
for (int i = 0; i < movies.length; i++) {
|
||||
List<Movie> options = searchMovie(movieFiles[i], locale);
|
||||
|
||||
// just use first result, if possible
|
||||
movies[i] = options.isEmpty() ? null : options.get(0);
|
||||
}
|
||||
|
||||
return movies;
|
||||
public Map<File, Movie> getMovieDescriptors(Collection<File> movieFiles, Locale locale) throws Exception {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -13,7 +13,6 @@ public class FastFile extends File {
|
|||
private Long length;
|
||||
private Boolean isDirectory;
|
||||
private Boolean isFile;
|
||||
private Boolean exists;
|
||||
|
||||
|
||||
public FastFile(String path) {
|
||||
|
@ -32,12 +31,6 @@ public class FastFile extends File {
|
|||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean exists() {
|
||||
return exists != null ? exists : (exists = super.exists());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isDirectory() {
|
||||
return isDirectory != null ? isDirectory : (isDirectory = super.isDirectory());
|
||||
|
|
|
@ -42,7 +42,7 @@ import com.ibm.icu.text.CharsetMatch;
|
|||
|
||||
public final class FileUtilities {
|
||||
|
||||
public static File renameFile(File source, File destination) throws IOException {
|
||||
public static File moveRename(File source, File destination) throws IOException {
|
||||
// resolve destination
|
||||
if (!destination.isAbsolute()) {
|
||||
// same folder, different name
|
||||
|
@ -57,25 +57,37 @@ public final class FileUtilities {
|
|||
throw new IOException("Failed to create folder: " + destinationFolder);
|
||||
}
|
||||
|
||||
if (source.isDirectory()) { // move folder
|
||||
moveFolderIO(source, destination);
|
||||
} else { // move file
|
||||
try {
|
||||
renameFileNIO2(source, destination);
|
||||
moveFileNIO2(source, destination);
|
||||
} catch (LinkageError e) {
|
||||
renameFileIO(source, destination);
|
||||
moveFileIO(source, destination);
|
||||
}
|
||||
}
|
||||
|
||||
return destination;
|
||||
}
|
||||
|
||||
|
||||
private static void renameFileNIO2(File source, File destination) throws IOException {
|
||||
private static void moveFileNIO2(File source, File destination) throws IOException {
|
||||
java.nio.file.Files.move(source.toPath(), destination.toPath());
|
||||
}
|
||||
|
||||
|
||||
private static void renameFileIO(File source, File destination) throws IOException {
|
||||
private static void moveFileIO(File source, File destination) throws IOException {
|
||||
if (!source.renameTo(destination)) {
|
||||
// try using Guava IO utilities, that'll just copy files if renameTo() fails
|
||||
com.google.common.io.Files.move(source, destination);
|
||||
// use "copy and delete" as fallback if standard rename fails
|
||||
org.apache.commons.io.FileUtils.moveFile(source, destination);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void moveFolderIO(File source, File destination) throws IOException {
|
||||
if (!source.renameTo(destination)) {
|
||||
// use "copy and delete" as fallback if standard move/rename fails
|
||||
org.apache.commons.io.FileUtils.moveDirectory(source, destination);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue