diff --git a/source/net/filebot/WebServices.java b/source/net/filebot/WebServices.java index 32ada97b..6f28954f 100644 --- a/source/net/filebot/WebServices.java +++ b/source/net/filebot/WebServices.java @@ -18,6 +18,7 @@ import java.util.concurrent.Future; import java.util.logging.Level; import java.util.logging.Logger; +import net.filebot.media.XattrMetaInfoProvider; import net.filebot.web.AcoustIDClient; import net.filebot.web.AnidbClient; import net.filebot.web.AnidbSearchResult; @@ -64,6 +65,7 @@ public final class WebServices { // misc 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 XattrMetaInfoProvider XattrMetaData = new XattrMetaInfoProvider(); public static EpisodeListProvider[] getEpisodeListProviders() { return new EpisodeListProvider[] { TheTVDB, AniDB, TVRage, Serienjunkies }; @@ -90,7 +92,6 @@ public final class WebServices { if (it.getName().equalsIgnoreCase(name)) return it; } - return null; // default } @@ -99,7 +100,6 @@ public final class WebServices { if (it.getName().equalsIgnoreCase(name)) return it; } - return null; // default } @@ -108,7 +108,6 @@ public final class WebServices { if (it.getName().equalsIgnoreCase(name)) return it; } - return null; // default } diff --git a/source/net/filebot/cli/ArgumentBean.java b/source/net/filebot/cli/ArgumentBean.java index cbaa2eca..32f5e60e 100644 --- a/source/net/filebot/cli/ArgumentBean.java +++ b/source/net/filebot/cli/ArgumentBean.java @@ -27,7 +27,7 @@ public class ArgumentBean { @Option(name = "-rename", usage = "Rename episode/movie files", metaVar = "fileset") 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; @Option(name = "--order", usage = "Episode order", metaVar = "[Airdate, Absolute, DVD]") diff --git a/source/net/filebot/cli/ArgumentProcessor.java b/source/net/filebot/cli/ArgumentProcessor.java index 39952cd4..6d4b86c1 100644 --- a/source/net/filebot/cli/ArgumentProcessor.java +++ b/source/net/filebot/cli/ArgumentProcessor.java @@ -111,6 +111,8 @@ public class ArgumentProcessor { // script finished successfully CLILogger.finest("Done ヾ(@⌒ー⌒@)ノ"); return 0; + } catch (CmdlineException e) { + CLILogger.log(Level.WARNING, e.getMessage()); } catch (ScriptDeath e) { CLILogger.log(Level.WARNING, e.getMessage(), e.getCause()); } catch (Throwable e) { diff --git a/source/net/filebot/cli/CmdlineException.java b/source/net/filebot/cli/CmdlineException.java new file mode 100644 index 00000000..975d3a3c --- /dev/null +++ b/source/net/filebot/cli/CmdlineException.java @@ -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); + } + +} diff --git a/source/net/filebot/cli/CmdlineOperations.java b/source/net/filebot/cli/CmdlineOperations.java index d75ef4fa..6f729223 100644 --- a/source/net/filebot/cli/CmdlineOperations.java +++ b/source/net/filebot/cli/CmdlineOperations.java @@ -50,6 +50,7 @@ import net.filebot.hash.HashType; import net.filebot.hash.VerificationFileReader; import net.filebot.hash.VerificationFileWriter; import net.filebot.media.MediaDetection; +import net.filebot.media.XattrMetaInfoProvider; import net.filebot.similarity.CommonSequenceMatcher; import net.filebot.similarity.EpisodeMatcher; 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)); } + if (XattrMetaData.getName().equalsIgnoreCase(db)) { + return renameByMetaData(files, action, conflictAction, outputDir, format, filter, XattrMetaData); + } + // auto-determine mode List mediaFiles = filter(files, VIDEO_FILES, SUBTITLE_FILES); double max = mediaFiles.size(); @@ -153,7 +158,7 @@ public class CmdlineOperations implements CmdlineInterface { List mediaFiles = filter(fileset, VIDEO_FILES, SUBTITLE_FILES); if (mediaFiles.isEmpty()) { - throw new Exception("No media files: " + files); + throw new CmdlineException("No media files: " + files); } // similarity metrics for matching @@ -185,7 +190,7 @@ public class CmdlineOperations implements CmdlineInterface { } 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) { @@ -210,7 +215,7 @@ public class CmdlineOperations implements CmdlineInterface { } 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 @@ -385,7 +390,7 @@ public class CmdlineOperations implements CmdlineInterface { List results = service.searchMovie(query, locale); List validResults = applyExpressionFilter(results, filter); 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 @@ -404,7 +409,7 @@ public class CmdlineOperations implements CmdlineInterface { // sanity check that we have something to do 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) @@ -536,6 +541,26 @@ public class CmdlineOperations implements CmdlineInterface { return renameAll(renameMap, renameAction, conflictAction, null); } + public List renameByMetaData(Collection 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 selection = sortByUniquePath(files); + Map renameMap = new LinkedHashMap(); + + for (Entry 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 getContext(final Collection> matches) { return new AbstractMap() { @@ -571,7 +596,7 @@ public class CmdlineOperations implements CmdlineInterface { public List renameAll(Map renameMap, RenameAction renameAction, ConflictAction conflictAction, List> matches) throws Exception { 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 @@ -591,7 +616,7 @@ public class CmdlineOperations implements CmdlineInterface { if (!destination.equals(source) && destination.exists()) { 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)) { @@ -677,7 +702,7 @@ public class CmdlineOperations implements CmdlineInterface { CLILogger.finest(String.format("Get [%s] subtitles for %d files", language.getName(), remainingVideos.size())); if (remainingVideos.isEmpty()) { - throw new Exception("No video files: " + files); + throw new CmdlineException("No video files: " + files); } // lookup subtitles by hash @@ -902,7 +927,7 @@ public class CmdlineOperations implements CmdlineInterface { } 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 @@ -921,7 +946,7 @@ public class CmdlineOperations implements CmdlineInterface { if (language == null) { // unable to lookup language - throw new Exception("Illegal language code: " + lang); + throw new CmdlineException("Illegal language code: " + lang); } return language; @@ -955,7 +980,7 @@ public class CmdlineOperations implements CmdlineInterface { File[] common = csm.matchFirstCommonSequence(pathArray); 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 @@ -976,11 +1001,11 @@ public class CmdlineOperations implements CmdlineInterface { } if (hashType == null) { - throw new Exception("Illegal output type: " + output); + throw new CmdlineException("Illegal output type: " + output); } 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)); @@ -994,7 +1019,7 @@ public class CmdlineOperations implements CmdlineInterface { // check if type is supported if (type == null) { - throw new Exception("Unsupported format: " + verificationFile); + throw new CmdlineException("Unsupported format: " + verificationFile); } // add all file names from verification file diff --git a/source/net/filebot/media/XattrMetaInfoProvider.java b/source/net/filebot/media/XattrMetaInfoProvider.java new file mode 100644 index 00000000..b012b671 --- /dev/null +++ b/source/net/filebot/media/XattrMetaInfoProvider.java @@ -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 getMetaData(Iterable files) { + Map result = new LinkedHashMap(); + + for (File f : files) { + Object metaObject = MediaDetection.readMetaInfo(f); + if (metaObject != null) { + result.put(f, metaObject); + } + } + + return result; + } + +}