2014-04-19 02:30:29 -04:00
package net.filebot.cli ;
2011-09-09 10:50:01 -04:00
2018-11-28 13:50:56 -05:00
import static java.awt.GraphicsEnvironment.* ;
2017-04-09 03:39:16 -04:00
import static java.util.Arrays.* ;
2011-09-09 10:50:01 -04:00
import static java.util.Collections.* ;
2016-03-09 15:36:28 -05:00
import static net.filebot.Logging.* ;
2016-11-27 17:10:42 -05:00
import static net.filebot.hash.VerificationUtilities.* ;
2018-05-21 05:33:58 -04:00
import static net.filebot.media.XattrMetaInfo.* ;
2016-11-27 17:10:42 -05:00
import static net.filebot.subtitle.SubtitleUtilities.* ;
2014-04-19 02:30:29 -04:00
import static net.filebot.util.FileUtilities.* ;
2018-12-05 07:24:18 -05:00
import static net.filebot.util.PGP.* ;
2011-09-09 10:50:01 -04:00
import java.io.File ;
2016-11-27 17:10:42 -05:00
import java.io.FileFilter ;
2016-10-18 13:02:51 -04:00
import java.io.StringWriter ;
2016-11-27 17:10:42 -05:00
import java.nio.charset.Charset ;
2017-01-15 15:20:02 -05:00
import java.nio.file.LinkOption ;
2011-09-09 10:50:01 -04:00
import java.util.ArrayList ;
2014-04-13 15:35:23 -04:00
import java.util.LinkedHashMap ;
2011-09-09 10:50:01 -04:00
import java.util.List ;
2014-04-13 15:35:23 -04:00
import java.util.Map ;
2016-11-27 17:10:42 -05:00
import java.util.Optional ;
import java.util.function.Supplier ;
2011-09-13 14:16:38 -04:00
import java.util.logging.Level ;
2017-04-09 03:39:16 -04:00
import java.util.regex.Pattern ;
2011-09-09 10:50:01 -04:00
import org.kohsuke.args4j.Argument ;
2014-04-13 15:35:23 -04:00
import org.kohsuke.args4j.CmdLineException ;
import org.kohsuke.args4j.CmdLineParser ;
2011-09-09 10:50:01 -04:00
import org.kohsuke.args4j.Option ;
2015-12-04 11:08:41 -05:00
import org.kohsuke.args4j.ParserProperties ;
2013-03-30 00:20:56 -04:00
import org.kohsuke.args4j.spi.ExplicitBooleanOptionHandler ;
2017-04-18 03:25:34 -04:00
import org.kohsuke.args4j.spi.RestOfArgumentsHandler ;
2011-09-09 10:50:01 -04:00
2017-01-28 06:07:12 -05:00
import net.filebot.ApplicationFolder ;
2016-03-27 09:52:59 -04:00
import net.filebot.Language ;
2017-04-05 04:15:34 -04:00
import net.filebot.RenameAction ;
2016-11-25 07:47:08 -05:00
import net.filebot.StandardRenameAction ;
2016-11-27 17:10:42 -05:00
import net.filebot.WebServices ;
import net.filebot.format.ExpressionFileFilter ;
2017-02-27 09:11:59 -05:00
import net.filebot.format.ExpressionFileFormat ;
2016-11-27 17:10:42 -05:00
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 ;
2017-04-09 03:39:16 -04:00
import net.filebot.ui.PanelBuilder ;
2016-11-27 17:10:42 -05:00
import net.filebot.web.Datasource ;
2017-02-23 07:10:12 -05:00
import net.filebot.web.EpisodeListProvider ;
2016-11-25 07:47:08 -05:00
import net.filebot.web.SortOrder ;
2016-03-27 09:52:59 -04:00
2011-09-09 10:50:01 -04:00
public class ArgumentBean {
2013-12-21 01:26:29 -05:00
2016-11-25 16:58:57 -05:00
@Option ( name = " --mode " , usage = " Open GUI in single panel mode / Enable CLI interactive mode " , metaVar = " [Rename, Subtitles, SFV] or [interactive] " )
2014-08-14 02:36:49 -04:00
public String mode = null ;
2016-03-10 14:23:12 -05:00
@Option ( name = " -rename " , usage = " Rename media files " )
2011-09-13 14:16:38 -04:00
public boolean rename = false ;
2013-12-21 01:26:29 -05:00
2016-03-28 16:45:43 -04:00
@Option ( name = " --db " , usage = " Database " , metaVar = " [TheTVDB, AniDB] or [TheMovieDB] or [AcoustID, ID3] or [xattr] " )
2011-09-14 14:13:34 -04:00
public String db ;
2013-12-21 01:26:29 -05:00
2012-02-20 10:40:49 -05:00
@Option ( name = " --order " , usage = " Episode order " , metaVar = " [Airdate, Absolute, DVD] " )
2012-02-13 04:54:57 -05:00
public String order = " Airdate " ;
2013-12-21 01:26:29 -05:00
2017-11-27 12:18:37 -05:00
@Option ( name = " --action " , usage = " Rename action " , metaVar = " [move, copy, keeplink, symlink, hardlink, clone, test] " )
2012-03-07 09:26:47 -05:00
public String action = " move " ;
2013-12-21 01:26:29 -05:00
2016-02-03 13:14:44 -05:00
@Option ( name = " --conflict " , usage = " Conflict resolution " , metaVar = " [skip, override, auto, index, fail] " )
2012-03-09 00:38:22 -05:00
public String conflict = " skip " ;
2013-12-21 01:26:29 -05:00
2018-06-28 12:54:04 -04:00
@Option ( name = " --filter " , usage = " Match filter expression " , metaVar = " {expression} " )
2012-03-24 22:50:28 -04:00
public String filter = null ;
2013-12-21 01:26:29 -05:00
2018-06-28 12:54:04 -04:00
@Option ( name = " --format " , usage = " Format expression " , metaVar = " {expression} " )
2011-09-14 14:13:34 -04:00
public String format ;
2013-12-21 01:26:29 -05:00
2016-03-10 14:23:12 -05:00
@Option ( name = " -non-strict " , usage = " Enable advanced matching and more aggressive guessing " )
2011-09-13 14:16:38 -04:00
public boolean nonStrict = false ;
2013-12-21 01:26:29 -05:00
2016-03-10 14:23:12 -05:00
@Option ( name = " -get-subtitles " , usage = " Fetch subtitles " )
2011-09-13 14:16:38 -04:00
public boolean getSubtitles ;
2013-12-21 01:26:29 -05:00
2017-04-20 12:49:04 -04:00
@Option ( name = " --q " , usage = " Force lookup query " , metaVar = " series / movie query " )
2011-09-14 14:13:34 -04:00
public String query ;
2013-12-21 01:26:29 -05:00
2017-04-20 12:49:04 -04:00
@Option ( name = " --lang " , usage = " Language " , metaVar = " language code " )
2011-09-09 10:50:01 -04:00
public String lang = " en " ;
2013-12-21 01:26:29 -05:00
2017-04-20 12:49:04 -04:00
@Option ( name = " -check " , usage = " Create / Check verification files " )
2011-09-13 14:16:38 -04:00
public boolean check ;
2013-12-21 01:26:29 -05:00
2017-04-20 12:49:04 -04:00
@Option ( name = " --output " , usage = " Output path " , metaVar = " path " )
2011-09-14 14:13:34 -04:00
public String output ;
2013-12-21 01:26:29 -05:00
2015-12-11 17:12:42 -05:00
@Option ( name = " --encoding " , usage = " Output character encoding " , metaVar = " [UTF-8, Windows-1252] " )
2011-09-14 14:13:34 -04:00
public String encoding ;
2013-12-21 01:26:29 -05:00
2017-04-20 12:49:04 -04:00
@Option ( name = " -list " , usage = " Print episode list " )
2011-11-03 07:56:29 -04:00
public boolean list = false ;
2013-12-21 01:26:29 -05:00
2017-04-20 12:49:04 -04:00
@Option ( name = " -mediainfo " , usage = " Print media info " )
2011-11-03 07:56:29 -04:00
public boolean mediaInfo = false ;
2013-12-21 01:26:29 -05:00
2016-03-10 13:32:11 -05:00
@Option ( name = " -revert " , usage = " Revert files " )
public boolean revert = false ;
2012-02-26 07:58:16 -05:00
@Option ( name = " -extract " , usage = " Extract archives " )
public boolean extract = false ;
2013-12-21 01:26:29 -05:00
2018-06-28 12:54:04 -04:00
@Option ( name = " -script " , usage = " Run Groovy script " , metaVar = " [fn:name] or [script.groovy] " )
2011-11-03 07:56:29 -04:00
public String script = null ;
2013-12-21 01:26:29 -05:00
2018-06-28 12:54:04 -04:00
@Option ( name = " --def " , usage = " Define script variables " , handler = BindingsHandler . class )
public Map < String , String > defines = new LinkedHashMap < String , String > ( ) ;
@Option ( name = " -r " , usage = " Recursively process folders " )
public boolean recursive = false ;
@Option ( name = " --file-filter " , usage = " Input file filter expression " , metaVar = " {expression} " )
public String inputFileFilter = null ;
@Option ( name = " -exec " , usage = " Execute command " , metaVar = " echo {f} [+] " , handler = RestOfArgumentsHandler . class )
public List < String > exec = new ArrayList < String > ( ) ;
@Option ( name = " -unixfs " , usage = " Allow special characters in file paths " )
public boolean unixfs = false ;
@Option ( name = " -no-xattr " , usage = " Disable extended attributes " )
public boolean disableExtendedAttributes = false ;
2016-10-19 13:01:47 -04:00
@Option ( name = " --log " , usage = " Log level " , metaVar = " [all, fine, info, warning] " )
2012-07-25 16:52:26 -04:00
public String log = " all " ;
2013-12-21 01:26:29 -05:00
2017-04-20 12:49:04 -04:00
@Option ( name = " --log-file " , usage = " Log file " , metaVar = " log.txt " )
2013-03-26 11:04:53 -04:00
public String logFile = null ;
2013-12-21 01:26:29 -05:00
2013-03-30 00:20:56 -04:00
@Option ( name = " --log-lock " , usage = " Lock log file " , metaVar = " [yes, no] " , handler = ExplicitBooleanOptionHandler . class )
public boolean logLock = true ;
2013-12-21 01:26:29 -05:00
2012-12-11 06:19:14 -05:00
@Option ( name = " -clear-cache " , usage = " Clear cached and temporary data " )
public boolean clearCache = false ;
2013-12-21 01:26:29 -05:00
2012-12-11 06:19:14 -05:00
@Option ( name = " -clear-prefs " , usage = " Clear application settings " )
public boolean clearPrefs = false ;
2013-12-21 01:26:29 -05:00
2011-11-28 22:14:39 -05:00
@Option ( name = " -version " , usage = " Print version identifier " )
public boolean version = false ;
2013-12-21 01:26:29 -05:00
2011-09-14 19:42:53 -04:00
@Option ( name = " -help " , usage = " Print this help message " )
public boolean help = false ;
2013-12-21 01:26:29 -05:00
2018-06-28 12:54:04 -04:00
@Option ( name = " --license " , usage = " Import license file " , metaVar = " file.psm " )
2018-06-10 05:16:51 -04:00
public String license = null ;
2012-07-28 17:17:29 -04:00
@Argument
2014-04-13 15:35:23 -04:00
public List < String > arguments = new ArrayList < String > ( ) ;
2013-12-21 01:26:29 -05:00
2011-09-09 10:50:01 -04:00
public boolean runCLI ( ) {
2018-11-28 13:55:03 -05:00
return rename | | getSubtitles | | check | | list | | mediaInfo | | revert | | extract | | script ! = null | | ( license ! = null & & ( isHeadless ( ) | | System . console ( ) ! = null ) ) ;
2011-09-09 10:50:01 -04:00
}
2013-12-21 01:26:29 -05:00
2016-11-24 15:13:39 -05:00
public boolean isInteractive ( ) {
return " interactive " . equalsIgnoreCase ( mode ) & & System . console ( ) ! = null ;
}
2011-11-28 22:14:39 -05:00
public boolean printVersion ( ) {
return version ;
}
2013-12-21 01:26:29 -05:00
2011-09-13 14:16:38 -04:00
public boolean printHelp ( ) {
return help ;
2011-09-09 10:50:01 -04:00
}
2013-12-21 01:26:29 -05:00
2012-12-11 06:19:14 -05:00
public boolean clearCache ( ) {
return clearCache ;
}
2013-12-21 01:26:29 -05:00
2011-09-13 14:16:38 -04:00
public boolean clearUserData ( ) {
2012-12-11 06:19:14 -05:00
return clearPrefs ;
2011-09-09 10:50:01 -04:00
}
2013-12-21 01:26:29 -05:00
2018-05-21 05:33:58 -04:00
public List < File > getFiles ( boolean resolveFolders ) throws Exception {
2012-07-28 17:17:29 -04:00
if ( arguments = = null | | arguments . isEmpty ( ) ) {
return emptyList ( ) ;
}
2013-12-21 01:26:29 -05:00
2011-09-13 14:16:38 -04:00
// resolve given paths
2012-07-28 17:17:29 -04:00
List < File > files = new ArrayList < File > ( ) ;
2013-12-21 01:26:29 -05:00
2016-10-04 11:47:10 -04:00
for ( String it : arguments ) {
// ignore empty arguments
2017-01-15 15:20:02 -05:00
if ( it . trim ( ) . isEmpty ( ) ) {
2016-10-04 11:47:10 -04:00
continue ;
2017-01-15 15:20:02 -05:00
}
2013-12-21 01:26:29 -05:00
2012-07-05 00:08:34 -04:00
// resolve relative paths
2016-10-04 11:47:10 -04:00
File file = new File ( it ) ;
2017-01-15 15:20:02 -05:00
// since we don't want to follow symlinks, we need to take the scenic route through the Path class
2012-07-05 00:08:34 -04:00
try {
2017-01-15 15:20:02 -05:00
file = file . toPath ( ) . toRealPath ( LinkOption . NOFOLLOW_LINKS ) . toFile ( ) ;
2012-07-05 00:08:34 -04:00
} catch ( Exception e ) {
2016-05-03 07:38:07 -04:00
debug . warning ( format ( " Illegal Argument: %s (%s) " , e , file ) ) ;
2012-07-05 00:08:34 -04:00
}
2013-12-21 01:26:29 -05:00
2014-04-17 11:35:33 -04:00
if ( resolveFolders & & file . isDirectory ( ) ) {
if ( recursive ) {
2016-10-04 11:47:51 -04:00
files . addAll ( listFiles ( file , FILES , HUMAN_NAME_ORDER ) ) ;
2014-04-17 11:35:33 -04:00
} else {
2016-10-05 02:56:33 -04:00
files . addAll ( getChildren ( file , f - > f . isFile ( ) & & ! f . isHidden ( ) , HUMAN_NAME_ORDER ) ) ;
2014-04-17 11:35:33 -04:00
}
} else {
files . add ( file ) ;
}
2011-09-13 14:16:38 -04:00
}
2013-12-21 01:26:29 -05:00
2018-05-21 05:33:58 -04:00
// input file filter (e.g. useful on Windows where find -exec is not an option)
if ( inputFileFilter ! = null ) {
return filter ( files , new ExpressionFileFilter ( inputFileFilter , f - > f ) ) ;
}
2011-09-13 14:16:38 -04:00
return files ;
2011-09-09 10:50:01 -04:00
}
2013-12-21 01:26:29 -05:00
2017-04-05 04:15:34 -04:00
public RenameAction getRenameAction ( ) {
2017-04-05 04:57:58 -04:00
// support custom executables (via absolute path)
if ( action . startsWith ( " / " ) ) {
2017-04-06 12:20:57 -04:00
return new ExecutableRenameAction ( action , getOutputPath ( ) ) ;
2017-04-05 04:57:58 -04:00
}
// support custom groovy scripts (via closures)
if ( action . startsWith ( " { " ) ) {
return new GroovyRenameAction ( action ) ;
}
2016-11-25 07:47:08 -05:00
return StandardRenameAction . forName ( action ) ;
}
public ConflictAction getConflictAction ( ) {
return ConflictAction . forName ( conflict ) ;
}
public SortOrder getSortOrder ( ) {
return SortOrder . forName ( order ) ;
}
2016-11-27 17:10:42 -05:00
public ExpressionFormat getExpressionFormat ( ) throws Exception {
return format = = null ? null : new ExpressionFormat ( format ) ;
}
2017-02-27 09:11:59 -05:00
public ExpressionFileFormat getExpressionFileFormat ( ) throws Exception {
return format = = null ? null : new ExpressionFileFormat ( format ) ;
}
2016-11-27 17:10:42 -05:00
public ExpressionFilter getExpressionFilter ( ) throws Exception {
return filter = = null ? null : new ExpressionFilter ( filter ) ;
}
2017-02-23 07:49:51 -05:00
public FileFilter getFileFilter ( ) throws Exception {
2018-05-21 05:33:58 -04:00
return filter = = null ? FILES : new ExpressionFileFilter ( filter , xattr : : getMetaInfo ) ;
2016-11-27 17:10:42 -05:00
}
public Datasource getDatasource ( ) {
return db = = null ? null : WebServices . getService ( db ) ;
}
2017-02-23 07:10:12 -05:00
public EpisodeListProvider getEpisodeListProvider ( ) {
return db = = null ? WebServices . TheTVDB : WebServices . getEpisodeListProvider ( db ) ; // default to TheTVDB if --db is not set
}
2016-11-27 17:10:42 -05:00
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 ) ;
2012-01-03 23:09:17 -05:00
}
2013-12-21 01:26:29 -05:00
2014-01-09 13:26:25 -05:00
public Language getLanguage ( ) {
2016-11-27 17:10:42 -05:00
// find language code for any input (en, eng, English, etc)
return optional ( lang ) . map ( Language : : findLanguage ) . orElseThrow ( error ( " Illegal language code " , lang ) ) ;
}
2017-01-28 06:07:12 -05:00
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 ) ;
}
2016-11-27 17:10:42 -05:00
public boolean isStrict ( ) {
return ! nonStrict ;
2014-01-09 13:26:25 -05:00
}
2011-10-29 16:24:01 -04:00
public Level getLogLevel ( ) {
return Level . parse ( log . toUpperCase ( ) ) ;
}
2013-12-21 01:26:29 -05:00
2017-04-18 03:25:34 -04:00
public ExecCommand getExecCommand ( ) {
try {
return exec = = null | | exec . isEmpty ( ) ? null : ExecCommand . parse ( exec , getOutputPath ( ) ) ;
} catch ( Exception e ) {
throw new CmdlineException ( " Illegal exec expression: " + exec ) ;
}
}
2017-04-09 03:39:16 -04:00
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 ) ;
2018-03-14 22:50:54 -04:00
PanelBuilder [ ] panel = stream ( PanelBuilder . defaultSequence ( ) ) . filter ( p - > pattern . matcher ( p . getName ( ) ) . matches ( ) ) . toArray ( PanelBuilder [ ] : : new ) ;
// throw exception if illegal pattern was passed in
if ( panel . length = = 0 ) {
return null ;
}
return panel ;
2017-04-09 03:39:16 -04:00
} ) . orElseThrow ( error ( " Illegal mode " , mode ) ) ;
}
2018-12-05 07:24:18 -05:00
public String getLicenseKey ( ) {
try {
return license = = null | | license . isEmpty ( ) ? null : findClearSignMessage ( readTextFile ( new File ( license ) ) ) ;
} catch ( Exception e ) {
throw new CmdlineException ( " Invalid License File: " + e . getMessage ( ) , e ) ;
}
2018-06-10 05:16:51 -04:00
}
2016-10-18 13:02:51 -04:00
private final String [ ] args ;
2014-01-05 01:58:31 -05:00
2018-08-09 02:58:04 -04:00
public ArgumentBean ( ) {
this . args = new String [ 0 ] ;
}
2018-12-17 04:43:02 -05:00
public ArgumentBean ( String [ ] args ) throws CmdLineException {
this . args = args . clone ( ) ;
2014-01-05 01:58:31 -05:00
2016-10-18 13:02:51 -04:00
CmdLineParser parser = new CmdLineParser ( this ) ;
parser . parseArgument ( args ) ;
2014-01-05 01:58:31 -05:00
}
2016-10-18 13:02:51 -04:00
public String [ ] getArgumentArray ( ) {
return args . clone ( ) ;
2014-04-13 15:35:23 -04:00
}
2016-10-18 13:02:51 -04:00
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 ( ) ;
2014-04-13 15:35:23 -04:00
}
2016-11-27 17:10:42 -05:00
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 ) ;
}
2018-12-17 04:43:02 -05:00
public static ArgumentBean parse ( String . . . args ) throws CmdLineException {
// MAS does not support or allow command-line applications and may run executables with strange arguments for no apparent reason (e.g. filebot.launcher -psn_0_774333) so we ignore arguments completely in this case
return Boolean . parseBoolean ( System . getProperty ( " apple.app.launcher " ) ) | | args = = null ? new ArgumentBean ( ) : new ArgumentBean ( args ) ;
}
2011-09-09 10:50:01 -04:00
}