From 94517baa9817e7b279a86c89ac30465bc2a7aee7 Mon Sep 17 00:00:00 2001 From: Reinhard Pointner Date: Thu, 10 Mar 2016 18:32:11 +0000 Subject: [PATCH] `filebot -revert /path` command --- source/net/filebot/Main.java | 2 +- source/net/filebot/cli/ArgumentBean.java | 5 +- source/net/filebot/cli/ArgumentProcessor.java | 166 ++++++++++-------- source/net/filebot/cli/CmdlineInterface.java | 2 + source/net/filebot/cli/CmdlineOperations.java | 45 +++-- 5 files changed, 133 insertions(+), 87 deletions(-) diff --git a/source/net/filebot/Main.java b/source/net/filebot/Main.java index 537ae77e..232f2bd6 100644 --- a/source/net/filebot/Main.java +++ b/source/net/filebot/Main.java @@ -116,7 +116,7 @@ public class Main { // CLI mode => run command-line interface and then exit if (args.runCLI()) { - int status = new ArgumentProcessor().process(args, new CmdlineOperations()); + int status = new ArgumentProcessor().run(args); System.exit(status); } diff --git a/source/net/filebot/cli/ArgumentBean.java b/source/net/filebot/cli/ArgumentBean.java index 2820bc6f..4b9a710d 100644 --- a/source/net/filebot/cli/ArgumentBean.java +++ b/source/net/filebot/cli/ArgumentBean.java @@ -75,6 +75,9 @@ public class ArgumentBean { @Option(name = "-mediainfo", usage = "Get media info") public boolean mediaInfo = false; + @Option(name = "-revert", usage = "Revert files") + public boolean revert = false; + @Option(name = "-extract", usage = "Extract archives") public boolean extract = false; @@ -118,7 +121,7 @@ public class ArgumentBean { public List arguments = new ArrayList(); public boolean runCLI() { - return rename || getSubtitles || check || list || mediaInfo || extract || script != null; + return rename || getSubtitles || check || list || mediaInfo || revert || extract || script != null; } public boolean printVersion() { diff --git a/source/net/filebot/cli/ArgumentProcessor.java b/source/net/filebot/cli/ArgumentProcessor.java index 98a1a347..2aa30291 100644 --- a/source/net/filebot/cli/ArgumentProcessor.java +++ b/source/net/filebot/cli/ArgumentProcessor.java @@ -11,6 +11,7 @@ import java.net.URI; import java.net.URL; import java.util.Collection; import java.util.LinkedHashSet; +import java.util.List; import java.util.MissingResourceException; import java.util.logging.Level; import java.util.regex.Matcher; @@ -28,87 +29,19 @@ import net.filebot.cli.ScriptShell.ScriptProvider; public class ArgumentProcessor { - public int process(ArgumentBean args, CmdlineInterface cli) { + public int run(ArgumentBean args) { try { - // print episode info - if (args.list) { - log.setLevel(Level.WARNING); // make sure to disable any logging on standard output - for (String eps : cli.fetchEpisodeList(args.query, args.format, args.db, args.order, args.filter, args.lang)) { - System.out.println(eps); - } - return 0; - } - - // print media info - if (args.mediaInfo) { - for (String line : cli.getMediaInfo(args.getFiles(true), args.format, args.filter)) { - System.out.println(line); - } - return 0; - } - - // execute CLI operations if (args.script == null) { - // sanity checks - if (args.getSubtitles && args.recursive) { - throw new CmdlineException("`filebot -get-subtitles -r` has been disabled due to abuse. Please see http://bit.ly/suball for details."); - } - - // file operations - Collection files = new LinkedHashSet(args.getFiles(true)); - - if (args.extract) { - files.addAll(cli.extract(files, args.output, args.conflict, null, true)); - } - - if (args.getSubtitles) { - files.addAll(cli.getMissingSubtitles(files, WebServices.OpenSubtitles.getName(), args.query, args.lang, args.output, args.encoding, args.format, !args.nonStrict)); - } - - if (args.rename) { - cli.rename(files, StandardRenameAction.forName(args.action), args.conflict, args.output, args.format, args.db, args.query, args.order, args.filter, args.lang, !args.nonStrict); - } - - if (args.check) { - // check verification file - if (containsOnly(files, MediaTypes.getDefaultFilter("verification"))) { - if (!cli.check(files)) { - throw new Exception("Data corruption detected"); // one or more hashes mismatch - } - } else { - cli.compute(files, args.output, args.encoding); - } - } + // execute command + return runCommand(args); } else { // execute user script - Bindings bindings = new SimpleBindings(); - bindings.put(ScriptShell.SHELL_ARGV_BINDING_NAME, args.getArray()); - bindings.put(ScriptShell.ARGV_BINDING_NAME, args.getFiles(false)); + runScript(args); - DefaultScriptProvider scriptProvider = new DefaultScriptProvider(); - URI script = scriptProvider.getScriptLocation(args.script); - - if (!scriptProvider.isInline(script)) { - if (scriptProvider.resolveTemplate(script) != null) { - scriptProvider.setBaseScheme(new URI(script.getScheme(), "%s", null)); - } else if (scriptProvider.isFile(script)) { - File parent = new File(script).getParentFile(); - File template = new File(parent, "%s.groovy"); - scriptProvider.setBaseScheme(template.toURI()); - } else { - File parent = new File(script.getPath()).getParentFile(); - String template = normalizePathSeparators(new File(parent, "%s.groovy").getPath()); - scriptProvider.setBaseScheme(new URI(script.getScheme(), script.getHost(), template, script.getQuery(), script.getFragment())); - } - } - - ScriptShell shell = new ScriptShell(scriptProvider, args.defines); - shell.runScript(script, bindings); + // script finished successfully + log.finest("Done ヾ(@⌒ー⌒@)ノ" + System.lineSeparator()); + return 0; } - - // script finished successfully - log.finest("Done ヾ(@⌒ー⌒@)ノ" + System.lineSeparator()); - return 0; } catch (Throwable e) { if (findCause(e, CmdlineException.class) != null) { log.log(Level.WARNING, findCause(e, CmdlineException.class).getMessage()); @@ -124,6 +57,89 @@ public class ArgumentProcessor { return 1; } + public int runCommand(ArgumentBean args) throws Exception { + CmdlineInterface cli = new CmdlineOperations(); + + // sanity checks + if (args.getSubtitles && args.recursive) { + throw new CmdlineException("`filebot -get-subtitles -r` has been disabled due to abuse. Please see http://bit.ly/suball for details."); + } + + // print episode info + if (args.list) { + List lines = cli.fetchEpisodeList(args.query, args.format, args.db, args.order, args.filter, args.lang); + lines.forEach(System.out::println); + return lines.isEmpty() ? 1 : 0; + } + + // print media info + if (args.mediaInfo) { + List lines = cli.getMediaInfo(args.getFiles(true), args.format, args.filter); + lines.forEach(System.out::println); + return lines.isEmpty() ? 1 : 0; + } + + // revert files + if (args.revert) { + List files = cli.revert(args.getFiles(false), args.filter, "TEST".equalsIgnoreCase(args.action)); + return files.isEmpty() ? 1 : 0; + } + + // file operations + Collection files = new LinkedHashSet(args.getFiles(true)); + + if (args.extract) { + files.addAll(cli.extract(files, args.output, args.conflict, null, true)); + } + + if (args.getSubtitles) { + files.addAll(cli.getMissingSubtitles(files, WebServices.OpenSubtitles.getName(), args.query, args.lang, args.output, args.encoding, args.format, !args.nonStrict)); + } + + if (args.rename) { + cli.rename(files, StandardRenameAction.forName(args.action), args.conflict, args.output, args.format, args.db, args.query, args.order, args.filter, args.lang, !args.nonStrict); + } + + if (args.check) { + // check verification file + if (containsOnly(files, MediaTypes.getDefaultFilter("verification"))) { + if (!cli.check(files)) { + throw new Exception("Data corruption detected"); // one or more hashes mismatch + } + } else { + cli.compute(files, args.output, args.encoding); + } + } + + return 0; + } + + public void runScript(ArgumentBean args) throws Throwable { + Bindings bindings = new SimpleBindings(); + bindings.put(ScriptShell.SHELL_ARGV_BINDING_NAME, args.getArray()); + bindings.put(ScriptShell.ARGV_BINDING_NAME, args.getFiles(false)); + + DefaultScriptProvider scriptProvider = new DefaultScriptProvider(); + URI script = scriptProvider.getScriptLocation(args.script); + + if (!scriptProvider.isInline(script)) { + if (scriptProvider.resolveTemplate(script) != null) { + scriptProvider.setBaseScheme(new URI(script.getScheme(), "%s", null)); + } else if (scriptProvider.isFile(script)) { + File parent = new File(script).getParentFile(); + File template = new File(parent, "%s.groovy"); + scriptProvider.setBaseScheme(template.toURI()); + } else { + File parent = new File(script.getPath()).getParentFile(); + String template = normalizePathSeparators(new File(parent, "%s.groovy").getPath()); + scriptProvider.setBaseScheme(new URI(script.getScheme(), script.getHost(), template, script.getQuery(), script.getFragment())); + } + } + + ScriptShell shell = new ScriptShell(scriptProvider, args.defines); + shell.runScript(script, bindings); + } + public static class DefaultScriptProvider implements ScriptProvider { public static final String SCHEME_REMOTE_STABLE = "fn"; diff --git a/source/net/filebot/cli/CmdlineInterface.java b/source/net/filebot/cli/CmdlineInterface.java index 6c2aaa20..e9ee8167 100644 --- a/source/net/filebot/cli/CmdlineInterface.java +++ b/source/net/filebot/cli/CmdlineInterface.java @@ -14,6 +14,8 @@ public interface CmdlineInterface { List rename(Map renameMap, RenameAction renameAction, String conflict) throws Exception; + List revert(Collection files, String filter, boolean test) throws Exception; + List getSubtitles(Collection files, String db, String query, String lang, String output, String encoding, String format, boolean strict) throws Exception; List getMissingSubtitles(Collection files, String db, String query, String lang, String output, String encoding, String format, boolean strict) throws Exception; diff --git a/source/net/filebot/cli/CmdlineOperations.java b/source/net/filebot/cli/CmdlineOperations.java index c831ff5e..ee17b167 100644 --- a/source/net/filebot/cli/CmdlineOperations.java +++ b/source/net/filebot/cli/CmdlineOperations.java @@ -3,6 +3,7 @@ package net.filebot.cli; import static java.nio.charset.StandardCharsets.*; import static java.util.Arrays.*; import static java.util.Collections.*; +import static java.util.stream.Collectors.*; import static net.filebot.Logging.*; import static net.filebot.MediaTypes.*; import static net.filebot.Settings.*; @@ -22,6 +23,7 @@ import java.util.AbstractMap; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; @@ -29,6 +31,7 @@ import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.NoSuchElementException; +import java.util.Objects; import java.util.Set; import java.util.SortedSet; import java.util.TreeMap; @@ -1070,24 +1073,45 @@ public class CmdlineOperations implements CmdlineInterface { @Override public List getMediaInfo(Collection files, String format, String filter) throws Exception { - if (filter != null && filter.length() > 0) { - ExpressionFileFilter includes = new ExpressionFileFilter(new ExpressionFilter(filter), false); - files = filter(files, includes); - - if (files.isEmpty()) { - throw new CmdlineException("No files: " + files); - } - } - ExpressionFormat formatter = new ExpressionFormat(format != null && format.length() > 0 ? format : "{fn} [{resolution} {vc} {channels} {ac} {minutes+'m'}]"); + FileFilter fileFilter = filter == null || filter.isEmpty() ? f -> true : new ExpressionFileFilter(new ExpressionFilter(filter), false); + List output = new ArrayList(); - for (File file : files) { + for (File file : filter(files, fileFilter)) { String line = formatter.format(new MediaBindingBean(readMetaInfo(file), file, null)); output.add(line); } return output; } + @Override + public List revert(Collection files, String filter, boolean test) throws Exception { + FileFilter fileFilter = filter == null || filter.isEmpty() ? f -> true : new ExpressionFileFilter(new ExpressionFilter(filter), false); + + Set whitelist = new HashSet(files); + Map history = HistorySpooler.getInstance().getCompleteHistory().getRenameMap(); + + return history.entrySet().stream().filter(it -> { + File current = it.getValue(); + return current.exists() && listPath(current).stream().anyMatch(whitelist::contains) && fileFilter.accept(current); + }).map(it -> { + File original = it.getKey(); + File current = it.getValue(); + + log.info(format("Revert [%s] to [%s]", current, original)); + if (test) { + return original; + } + + try { + return StandardRenameAction.revert(current, original); + } catch (Exception e) { + log.warning(format("Failed to revert file: %s", e.getMessage())); + return null; + } + }).filter(Objects::nonNull).collect(toList()); + } + @Override public List extract(Collection files, String output, String conflict, FileFilter filter, boolean forceExtractAll) throws Exception { ConflictAction conflictAction = ConflictAction.forName(conflict); @@ -1170,4 +1194,5 @@ public class CmdlineOperations implements CmdlineInterface { return extractedFiles; } + }