mirror of
https://github.com/mitb-archive/filebot
synced 2024-11-11 03:45:06 -05:00
352 lines
11 KiB
Java
352 lines
11 KiB
Java
package net.filebot.cli;
|
|
|
|
import static java.util.Arrays.*;
|
|
import static java.util.Collections.*;
|
|
import static net.filebot.Logging.*;
|
|
import static net.filebot.hash.VerificationUtilities.*;
|
|
import static net.filebot.subtitle.SubtitleUtilities.*;
|
|
import static net.filebot.util.FileUtilities.*;
|
|
|
|
import java.io.File;
|
|
import java.io.FileFilter;
|
|
import java.io.StringWriter;
|
|
import java.nio.charset.Charset;
|
|
import java.nio.file.LinkOption;
|
|
import java.util.ArrayList;
|
|
import java.util.LinkedHashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Optional;
|
|
import java.util.function.Supplier;
|
|
import java.util.logging.Level;
|
|
import java.util.regex.Pattern;
|
|
|
|
import org.kohsuke.args4j.Argument;
|
|
import org.kohsuke.args4j.CmdLineException;
|
|
import org.kohsuke.args4j.CmdLineParser;
|
|
import org.kohsuke.args4j.Option;
|
|
import org.kohsuke.args4j.ParserProperties;
|
|
import org.kohsuke.args4j.spi.ExplicitBooleanOptionHandler;
|
|
|
|
import net.filebot.ApplicationFolder;
|
|
import net.filebot.Language;
|
|
import net.filebot.RenameAction;
|
|
import net.filebot.StandardRenameAction;
|
|
import net.filebot.WebServices;
|
|
import net.filebot.format.ExpressionFileFilter;
|
|
import net.filebot.format.ExpressionFileFormat;
|
|
import net.filebot.format.ExpressionFilter;
|
|
import net.filebot.format.ExpressionFormat;
|
|
import net.filebot.hash.HashType;
|
|
import net.filebot.subtitle.SubtitleFormat;
|
|
import net.filebot.subtitle.SubtitleNaming;
|
|
import net.filebot.ui.PanelBuilder;
|
|
import net.filebot.web.Datasource;
|
|
import net.filebot.web.EpisodeListProvider;
|
|
import net.filebot.web.SortOrder;
|
|
|
|
public class ArgumentBean {
|
|
|
|
@Option(name = "--mode", usage = "Open GUI in single panel mode / Enable CLI interactive mode", metaVar = "[Rename, Subtitles, SFV] or [interactive]")
|
|
public String mode = null;
|
|
|
|
@Option(name = "-rename", usage = "Rename media files")
|
|
public boolean rename = false;
|
|
|
|
@Option(name = "--db", usage = "Database", metaVar = "[TheTVDB, AniDB] or [TheMovieDB] or [AcoustID, ID3] or [xattr]")
|
|
public String db;
|
|
|
|
@Option(name = "--order", usage = "Episode order", metaVar = "[Airdate, Absolute, DVD]")
|
|
public String order = "Airdate";
|
|
|
|
@Option(name = "--action", usage = "Rename action", metaVar = "[move, copy, keeplink, symlink, hardlink, reflink, test]")
|
|
public String action = "move";
|
|
|
|
@Option(name = "--conflict", usage = "Conflict resolution", metaVar = "[skip, override, auto, index, fail]")
|
|
public String conflict = "skip";
|
|
|
|
@Option(name = "--filter", usage = "Filter expression", metaVar = "expression")
|
|
public String filter = null;
|
|
|
|
@Option(name = "--format", usage = "Format expression", metaVar = "expression")
|
|
public String format;
|
|
|
|
@Option(name = "-non-strict", usage = "Enable advanced matching and more aggressive guessing")
|
|
public boolean nonStrict = false;
|
|
|
|
@Option(name = "-get-subtitles", usage = "Fetch subtitles")
|
|
public boolean getSubtitles;
|
|
|
|
@Option(name = "--q", usage = "Force lookup query", metaVar = "series/movie title")
|
|
public String query;
|
|
|
|
@Option(name = "--lang", usage = "Language", metaVar = "3-letter language code")
|
|
public String lang = "en";
|
|
|
|
@Option(name = "-check", usage = "Create/Check verification files")
|
|
public boolean check;
|
|
|
|
@Option(name = "--output", usage = "Output path", metaVar = "/path")
|
|
public String output;
|
|
|
|
@Option(name = "--encoding", usage = "Output character encoding", metaVar = "[UTF-8, Windows-1252]")
|
|
public String encoding;
|
|
|
|
@Option(name = "-list", usage = "Fetch episode list")
|
|
public boolean list = false;
|
|
|
|
@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;
|
|
|
|
@Option(name = "-script", usage = "Run Groovy script", metaVar = "[fn:name] or [dev:name] or [/path/to/script.groovy]")
|
|
public String script = null;
|
|
|
|
@Option(name = "--log", usage = "Log level", metaVar = "[all, fine, info, warning]")
|
|
public String log = "all";
|
|
|
|
@Option(name = "--log-file", usage = "Log file", metaVar = "/path/to/log.txt")
|
|
public String logFile = null;
|
|
|
|
@Option(name = "--log-lock", usage = "Lock log file", metaVar = "[yes, no]", handler = ExplicitBooleanOptionHandler.class)
|
|
public boolean logLock = true;
|
|
|
|
@Option(name = "-r", usage = "Recursively process folders")
|
|
public boolean recursive = false;
|
|
|
|
@Option(name = "-clear-cache", usage = "Clear cached and temporary data")
|
|
public boolean clearCache = false;
|
|
|
|
@Option(name = "-clear-prefs", usage = "Clear application settings")
|
|
public boolean clearPrefs = false;
|
|
|
|
@Option(name = "-unixfs", usage = "Do not strip invalid characters from file paths")
|
|
public boolean unixfs = false;
|
|
|
|
@Option(name = "-no-xattr", usage = "Disable extended attributes")
|
|
public boolean disableExtendedAttributes = false;
|
|
|
|
@Option(name = "-version", usage = "Print version identifier")
|
|
public boolean version = false;
|
|
|
|
@Option(name = "-help", usage = "Print this help message")
|
|
public boolean help = false;
|
|
|
|
@Option(name = "--def", usage = "Define script variables", handler = BindingsHandler.class)
|
|
public Map<String, String> defines = new LinkedHashMap<String, String>();
|
|
|
|
@Argument
|
|
public List<String> arguments = new ArrayList<String>();
|
|
|
|
public boolean runCLI() {
|
|
return rename || getSubtitles || check || list || mediaInfo || revert || extract || script != null;
|
|
}
|
|
|
|
public boolean isInteractive() {
|
|
return "interactive".equalsIgnoreCase(mode) && System.console() != null;
|
|
}
|
|
|
|
public boolean printVersion() {
|
|
return version;
|
|
}
|
|
|
|
public boolean printHelp() {
|
|
return help;
|
|
}
|
|
|
|
public boolean clearCache() {
|
|
return clearCache;
|
|
}
|
|
|
|
public boolean clearUserData() {
|
|
return clearPrefs;
|
|
}
|
|
|
|
public List<File> getFiles(boolean resolveFolders) {
|
|
if (arguments == null || arguments.isEmpty()) {
|
|
return emptyList();
|
|
}
|
|
|
|
// resolve given paths
|
|
List<File> files = new ArrayList<File>();
|
|
|
|
for (String it : arguments) {
|
|
// ignore empty arguments
|
|
if (it.trim().isEmpty()) {
|
|
continue;
|
|
}
|
|
|
|
// resolve relative paths
|
|
File file = new File(it);
|
|
|
|
// since we don't want to follow symlinks, we need to take the scenic route through the Path class
|
|
try {
|
|
file = file.toPath().toRealPath(LinkOption.NOFOLLOW_LINKS).toFile();
|
|
} catch (Exception e) {
|
|
debug.warning(format("Illegal Argument: %s (%s)", e, file));
|
|
}
|
|
|
|
if (resolveFolders && file.isDirectory()) {
|
|
if (recursive) {
|
|
files.addAll(listFiles(file, FILES, HUMAN_NAME_ORDER));
|
|
} else {
|
|
files.addAll(getChildren(file, f -> f.isFile() && !f.isHidden(), HUMAN_NAME_ORDER));
|
|
}
|
|
} else {
|
|
files.add(file);
|
|
}
|
|
}
|
|
|
|
return files;
|
|
}
|
|
|
|
public RenameAction getRenameAction() {
|
|
// support custom executables (via absolute path)
|
|
if (action.startsWith("/")) {
|
|
return new ExecutableRenameAction(action, getOutputPath());
|
|
}
|
|
|
|
// support custom groovy scripts (via closures)
|
|
if (action.startsWith("{")) {
|
|
return new GroovyRenameAction(action);
|
|
}
|
|
|
|
return StandardRenameAction.forName(action);
|
|
}
|
|
|
|
public ConflictAction getConflictAction() {
|
|
return ConflictAction.forName(conflict);
|
|
}
|
|
|
|
public SortOrder getSortOrder() {
|
|
return SortOrder.forName(order);
|
|
}
|
|
|
|
public ExpressionFormat getExpressionFormat() throws Exception {
|
|
return format == null ? null : new ExpressionFormat(format);
|
|
}
|
|
|
|
public ExpressionFileFormat getExpressionFileFormat() throws Exception {
|
|
return format == null ? null : new ExpressionFileFormat(format);
|
|
}
|
|
|
|
public ExpressionFilter getExpressionFilter() throws Exception {
|
|
return filter == null ? null : new ExpressionFilter(filter);
|
|
}
|
|
|
|
public FileFilter getFileFilter() throws Exception {
|
|
return filter == null ? FILES : new ExpressionFileFilter(filter);
|
|
}
|
|
|
|
public Datasource getDatasource() {
|
|
return db == null ? null : WebServices.getService(db);
|
|
}
|
|
|
|
public EpisodeListProvider getEpisodeListProvider() {
|
|
return db == null ? WebServices.TheTVDB : WebServices.getEpisodeListProvider(db); // default to TheTVDB if --db is not set
|
|
}
|
|
|
|
public String getSearchQuery() {
|
|
return query == null || query.isEmpty() ? null : query;
|
|
}
|
|
|
|
public File getOutputPath() {
|
|
return output == null ? null : new File(output);
|
|
}
|
|
|
|
public File getAbsoluteOutputFolder() throws Exception {
|
|
return output == null ? null : new File(output).getCanonicalFile();
|
|
}
|
|
|
|
public SubtitleFormat getSubtitleOutputFormat() {
|
|
return output == null ? null : getSubtitleFormatByName(output);
|
|
}
|
|
|
|
public SubtitleNaming getSubtitleNamingFormat() {
|
|
return optional(format).map(SubtitleNaming::forName).orElse(SubtitleNaming.MATCH_VIDEO_ADD_LANGUAGE_TAG);
|
|
}
|
|
|
|
public HashType getOutputHashType() {
|
|
// support --output checksum.sfv
|
|
return optional(output).map(File::new).map(f -> getHashType(f)).orElseGet(() -> {
|
|
// support --format SFV
|
|
return optional(format).map(k -> getHashTypeByExtension(k)).orElse(HashType.SFV);
|
|
});
|
|
}
|
|
|
|
public Charset getEncoding() {
|
|
return encoding == null ? null : Charset.forName(encoding);
|
|
}
|
|
|
|
public Language getLanguage() {
|
|
// find language code for any input (en, eng, English, etc)
|
|
return optional(lang).map(Language::findLanguage).orElseThrow(error("Illegal language code", lang));
|
|
}
|
|
|
|
public File getLogFile() {
|
|
File file = new File(logFile);
|
|
|
|
if (file.isAbsolute()) {
|
|
return file;
|
|
}
|
|
|
|
// by default resolve relative paths against {applicationFolder}/logs/{logFile}
|
|
return ApplicationFolder.AppData.resolve("logs/" + logFile);
|
|
}
|
|
|
|
public boolean isStrict() {
|
|
return !nonStrict;
|
|
}
|
|
|
|
public Level getLogLevel() {
|
|
return Level.parse(log.toUpperCase());
|
|
}
|
|
|
|
public PanelBuilder[] getPanelBuilders() {
|
|
// default multi panel mode
|
|
if (mode == null) {
|
|
return PanelBuilder.defaultSequence();
|
|
}
|
|
|
|
// only selected panels
|
|
return optional(mode).map(m -> {
|
|
Pattern pattern = Pattern.compile(mode, Pattern.CASE_INSENSITIVE);
|
|
return stream(PanelBuilder.defaultSequence()).filter(p -> pattern.matcher(p.getName()).matches()).toArray(PanelBuilder[]::new);
|
|
}).orElseThrow(error("Illegal mode", mode));
|
|
}
|
|
|
|
private final String[] args;
|
|
|
|
public ArgumentBean(String... args) throws CmdLineException {
|
|
this.args = args;
|
|
|
|
CmdLineParser parser = new CmdLineParser(this);
|
|
parser.parseArgument(args);
|
|
}
|
|
|
|
public String[] getArgumentArray() {
|
|
return args.clone();
|
|
}
|
|
|
|
public String usage() {
|
|
StringWriter buffer = new StringWriter();
|
|
CmdLineParser parser = new CmdLineParser(this, ParserProperties.defaults().withShowDefaults(false).withOptionSorter(null));
|
|
parser.printUsage(buffer, null);
|
|
return buffer.toString();
|
|
}
|
|
|
|
private static <T> Optional<T> optional(T value) {
|
|
return Optional.ofNullable(value);
|
|
}
|
|
|
|
private static Supplier<CmdlineException> error(String message, Object value) {
|
|
return () -> new CmdlineException(message + ": " + value);
|
|
}
|
|
|
|
}
|