+ support for --db xattr to allow offline renaming directly using previously written xattr metadata

This commit is contained in:
Reinhard Pointner 2014-07-17 07:08:23 +00:00
parent d9ea19f259
commit 76073cfb9d
6 changed files with 83 additions and 18 deletions

View File

@ -18,6 +18,7 @@ import java.util.concurrent.Future;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import net.filebot.media.XattrMetaInfoProvider;
import net.filebot.web.AcoustIDClient; import net.filebot.web.AcoustIDClient;
import net.filebot.web.AnidbClient; import net.filebot.web.AnidbClient;
import net.filebot.web.AnidbSearchResult; import net.filebot.web.AnidbSearchResult;
@ -64,6 +65,7 @@ public final class WebServices {
// misc // misc
public static final FanartTVClient FanartTV = new FanartTVClient(Settings.getApplicationProperty("fanart.tv.apikey")); public static final FanartTVClient FanartTV = new FanartTVClient(Settings.getApplicationProperty("fanart.tv.apikey"));
public static final AcoustIDClient AcoustID = new AcoustIDClient(Settings.getApplicationProperty("acoustid.apikey")); public static final AcoustIDClient AcoustID = new AcoustIDClient(Settings.getApplicationProperty("acoustid.apikey"));
public static final XattrMetaInfoProvider XattrMetaData = new XattrMetaInfoProvider();
public static EpisodeListProvider[] getEpisodeListProviders() { public static EpisodeListProvider[] getEpisodeListProviders() {
return new EpisodeListProvider[] { TheTVDB, AniDB, TVRage, Serienjunkies }; return new EpisodeListProvider[] { TheTVDB, AniDB, TVRage, Serienjunkies };
@ -90,7 +92,6 @@ public final class WebServices {
if (it.getName().equalsIgnoreCase(name)) if (it.getName().equalsIgnoreCase(name))
return it; return it;
} }
return null; // default return null; // default
} }
@ -99,7 +100,6 @@ public final class WebServices {
if (it.getName().equalsIgnoreCase(name)) if (it.getName().equalsIgnoreCase(name))
return it; return it;
} }
return null; // default return null; // default
} }
@ -108,7 +108,6 @@ public final class WebServices {
if (it.getName().equalsIgnoreCase(name)) if (it.getName().equalsIgnoreCase(name))
return it; return it;
} }
return null; // default return null; // default
} }

View File

@ -27,7 +27,7 @@ public class ArgumentBean {
@Option(name = "-rename", usage = "Rename episode/movie files", metaVar = "fileset") @Option(name = "-rename", usage = "Rename episode/movie files", metaVar = "fileset")
public boolean rename = false; public boolean rename = false;
@Option(name = "--db", usage = "Episode/Movie database", metaVar = "[TheTVDB, AniDB, TVRage] or [TheMovieDB, IMDb]") @Option(name = "--db", usage = "Episode/Movie database", metaVar = "[TheTVDB, AniDB, TVRage] or [TheMovieDB, IMDb] or [xattr]")
public String db; public String db;
@Option(name = "--order", usage = "Episode order", metaVar = "[Airdate, Absolute, DVD]") @Option(name = "--order", usage = "Episode order", metaVar = "[Airdate, Absolute, DVD]")

View File

@ -111,6 +111,8 @@ public class ArgumentProcessor {
// script finished successfully // script finished successfully
CLILogger.finest("Done ヾ(@⌒ー⌒@)"); CLILogger.finest("Done ヾ(@⌒ー⌒@)");
return 0; return 0;
} catch (CmdlineException e) {
CLILogger.log(Level.WARNING, e.getMessage());
} catch (ScriptDeath e) { } catch (ScriptDeath e) {
CLILogger.log(Level.WARNING, e.getMessage(), e.getCause()); CLILogger.log(Level.WARNING, e.getMessage(), e.getCause());
} catch (Throwable e) { } catch (Throwable e) {

View File

@ -0,0 +1,13 @@
package net.filebot.cli;
public class CmdlineException extends RuntimeException {
public CmdlineException(String message) {
super(message);
}
public CmdlineException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@ -50,6 +50,7 @@ import net.filebot.hash.HashType;
import net.filebot.hash.VerificationFileReader; import net.filebot.hash.VerificationFileReader;
import net.filebot.hash.VerificationFileWriter; import net.filebot.hash.VerificationFileWriter;
import net.filebot.media.MediaDetection; import net.filebot.media.MediaDetection;
import net.filebot.media.XattrMetaInfoProvider;
import net.filebot.similarity.CommonSequenceMatcher; import net.filebot.similarity.CommonSequenceMatcher;
import net.filebot.similarity.EpisodeMatcher; import net.filebot.similarity.EpisodeMatcher;
import net.filebot.similarity.Match; import net.filebot.similarity.Match;
@ -104,6 +105,10 @@ public class CmdlineOperations implements CmdlineInterface {
return renameMusic(files, action, conflictAction, outputDir, format, getMusicIdentificationService(db) == null ? AcoustID : getMusicIdentificationService(db)); return renameMusic(files, action, conflictAction, outputDir, format, getMusicIdentificationService(db) == null ? AcoustID : getMusicIdentificationService(db));
} }
if (XattrMetaData.getName().equalsIgnoreCase(db)) {
return renameByMetaData(files, action, conflictAction, outputDir, format, filter, XattrMetaData);
}
// auto-determine mode // auto-determine mode
List<File> mediaFiles = filter(files, VIDEO_FILES, SUBTITLE_FILES); List<File> mediaFiles = filter(files, VIDEO_FILES, SUBTITLE_FILES);
double max = mediaFiles.size(); double max = mediaFiles.size();
@ -153,7 +158,7 @@ public class CmdlineOperations implements CmdlineInterface {
List<File> mediaFiles = filter(fileset, VIDEO_FILES, SUBTITLE_FILES); List<File> mediaFiles = filter(fileset, VIDEO_FILES, SUBTITLE_FILES);
if (mediaFiles.isEmpty()) { if (mediaFiles.isEmpty()) {
throw new Exception("No media files: " + files); throw new CmdlineException("No media files: " + files);
} }
// similarity metrics for matching // similarity metrics for matching
@ -185,7 +190,7 @@ public class CmdlineOperations implements CmdlineInterface {
} }
if (strict && seriesNames.size() > 1) { if (strict && seriesNames.size() > 1) {
throw new Exception("Processing multiple shows at once requires -non-strict"); throw new CmdlineException("Processing multiple shows at once requires -non-strict");
} }
if (seriesNames.size() == 0) { if (seriesNames.size() == 0) {
@ -210,7 +215,7 @@ public class CmdlineOperations implements CmdlineInterface {
} }
if (matches.isEmpty()) { if (matches.isEmpty()) {
throw new Exception("Unable to match files to episode data"); throw new CmdlineException("Unable to match files to episode data");
} }
// handle derived files // handle derived files
@ -385,7 +390,7 @@ public class CmdlineOperations implements CmdlineInterface {
List<Movie> results = service.searchMovie(query, locale); List<Movie> results = service.searchMovie(query, locale);
List<Movie> validResults = applyExpressionFilter(results, filter); List<Movie> validResults = applyExpressionFilter(results, filter);
if (validResults.isEmpty()) { if (validResults.isEmpty()) {
throw new Exception("Unable to find a valid match: " + results); throw new CmdlineException("Unable to find a valid match: " + results);
} }
// force all mappings // force all mappings
@ -404,7 +409,7 @@ public class CmdlineOperations implements CmdlineInterface {
// sanity check that we have something to do // sanity check that we have something to do
if (fileset.isEmpty() || movieMatchFiles.isEmpty()) { if (fileset.isEmpty() || movieMatchFiles.isEmpty()) {
throw new Exception("No media files: " + files); throw new CmdlineException("No media files: " + files);
} }
// map movies to (possibly multiple) files (in natural order) // map movies to (possibly multiple) files (in natural order)
@ -536,6 +541,26 @@ public class CmdlineOperations implements CmdlineInterface {
return renameAll(renameMap, renameAction, conflictAction, null); return renameAll(renameMap, renameAction, conflictAction, null);
} }
public List<File> renameByMetaData(Collection<File> files, RenameAction renameAction, ConflictAction conflictAction, File outputDir, ExpressionFormat format, ExpressionFilter filter, XattrMetaInfoProvider service) throws Exception {
CLILogger.config(format("Rename files using [%s]", service.getName()));
// force sort order
List<File> selection = sortByUniquePath(files);
Map<File, File> renameMap = new LinkedHashMap<File, File>();
for (Entry<File, Object> it : service.getMetaData(selection).entrySet()) {
MediaBindingBean bindingBean = new MediaBindingBean(it.getValue(), it.getKey(), null);
if (filter == null || filter.matches(bindingBean)) {
String newName = (format != null) ? format.format(bindingBean) : validateFileName(it.getValue().toString());
renameMap.put(it.getKey(), getDestinationFile(it.getKey(), newName, outputDir));
}
}
// rename files according to xattr metadata objects
return renameAll(renameMap, renameAction, conflictAction, null);
}
private Map<File, Object> getContext(final Collection<Match<File, ?>> matches) { private Map<File, Object> getContext(final Collection<Match<File, ?>> matches) {
return new AbstractMap<File, Object>() { return new AbstractMap<File, Object>() {
@ -571,7 +596,7 @@ public class CmdlineOperations implements CmdlineInterface {
public List<File> renameAll(Map<File, File> renameMap, RenameAction renameAction, ConflictAction conflictAction, List<Match<File, ?>> matches) throws Exception { public List<File> renameAll(Map<File, File> renameMap, RenameAction renameAction, ConflictAction conflictAction, List<Match<File, ?>> matches) throws Exception {
if (renameMap.isEmpty()) { if (renameMap.isEmpty()) {
throw new Exception("Unable to identify or process any files"); throw new CmdlineException("Unable to identify or process any files");
} }
// rename files // rename files
@ -591,7 +616,7 @@ public class CmdlineOperations implements CmdlineInterface {
if (!destination.equals(source) && destination.exists()) { if (!destination.equals(source) && destination.exists()) {
if (conflictAction == ConflictAction.FAIL) { if (conflictAction == ConflictAction.FAIL) {
throw new Exception("File already exists: " + destination); throw new CmdlineException("File already exists: " + destination);
} }
if (conflictAction == ConflictAction.OVERRIDE || (conflictAction == ConflictAction.AUTO && VIDEO_SIZE_ORDER.compare(source, destination) > 0)) { if (conflictAction == ConflictAction.OVERRIDE || (conflictAction == ConflictAction.AUTO && VIDEO_SIZE_ORDER.compare(source, destination) > 0)) {
@ -677,7 +702,7 @@ public class CmdlineOperations implements CmdlineInterface {
CLILogger.finest(String.format("Get [%s] subtitles for %d files", language.getName(), remainingVideos.size())); CLILogger.finest(String.format("Get [%s] subtitles for %d files", language.getName(), remainingVideos.size()));
if (remainingVideos.isEmpty()) { if (remainingVideos.isEmpty()) {
throw new Exception("No video files: " + files); throw new CmdlineException("No video files: " + files);
} }
// lookup subtitles by hash // lookup subtitles by hash
@ -902,7 +927,7 @@ public class CmdlineOperations implements CmdlineInterface {
} }
if (strict) { if (strict) {
throw new Exception("Multiple options: Force auto-select requires non-strict matching: " + searchResults); throw new CmdlineException("Multiple options: Force auto-select requires non-strict matching: " + searchResults);
} }
// just pick the best 5 matches // just pick the best 5 matches
@ -921,7 +946,7 @@ public class CmdlineOperations implements CmdlineInterface {
if (language == null) { if (language == null) {
// unable to lookup language // unable to lookup language
throw new Exception("Illegal language code: " + lang); throw new CmdlineException("Illegal language code: " + lang);
} }
return language; return language;
@ -955,7 +980,7 @@ public class CmdlineOperations implements CmdlineInterface {
File[] common = csm.matchFirstCommonSequence(pathArray); File[] common = csm.matchFirstCommonSequence(pathArray);
if (common == null) { if (common == null) {
throw new Exception("Paths must be on the same filesystem: " + files); throw new CmdlineException("Paths must be on the same filesystem: " + files);
} }
// last element in the common sequence must be the root folder // last element in the common sequence must be the root folder
@ -976,11 +1001,11 @@ public class CmdlineOperations implements CmdlineInterface {
} }
if (hashType == null) { if (hashType == null) {
throw new Exception("Illegal output type: " + output); throw new CmdlineException("Illegal output type: " + output);
} }
if (files.isEmpty()) { if (files.isEmpty()) {
throw new Exception("No files: " + files); throw new CmdlineException("No files: " + files);
} }
CLILogger.info(format("Compute %s hash for %s files [%s]", hashType, files.size(), outputFile)); CLILogger.info(format("Compute %s hash for %s files [%s]", hashType, files.size(), outputFile));
@ -994,7 +1019,7 @@ public class CmdlineOperations implements CmdlineInterface {
// check if type is supported // check if type is supported
if (type == null) { if (type == null) {
throw new Exception("Unsupported format: " + verificationFile); throw new CmdlineException("Unsupported format: " + verificationFile);
} }
// add all file names from verification file // add all file names from verification file

View File

@ -0,0 +1,26 @@
package net.filebot.media;
import java.io.File;
import java.util.LinkedHashMap;
import java.util.Map;
public class XattrMetaInfoProvider {
public String getName() {
return "xattr";
}
public Map<File, Object> getMetaData(Iterable<File> files) {
Map<File, Object> result = new LinkedHashMap<File, Object>();
for (File f : files) {
Object metaObject = MediaDetection.readMetaInfo(f);
if (metaObject != null) {
result.put(f, metaObject);
}
}
return result;
}
}