1
0
mirror of https://github.com/mitb-archive/filebot synced 2024-12-21 23:38:50 -05:00

Add bash completion

This commit is contained in:
Reinhard Pointner 2019-06-06 23:43:21 +07:00
parent a8ae0b2623
commit 4db119cd29
2 changed files with 402 additions and 0 deletions

View File

@ -0,0 +1,200 @@
#!/usr/bin/env bash
#
# filebot Bash Completion
# =======================
#
# Bash completion support for the `filebot` command,
# generated by [picocli](http://picocli.info/) version 4.0.0-beta-1b.
#
# Installation
# ------------
#
# 1. Source all completion scripts in your .bash_profile
#
# cd $YOUR_APP_HOME/bin
# for f in $(find . -name "*_completion"); do line=". $(pwd)/$f"; grep "$line" ~/.bash_profile || echo "$line" >> ~/.bash_profile; done
#
# 2. Open a new bash console, and type `filebot [TAB][TAB]`
#
# 1a. Alternatively, if you have [bash-completion](https://github.com/scop/bash-completion) installed:
# Place this file in a `bash-completion.d` folder:
#
# * /etc/bash-completion.d
# * /usr/local/etc/bash-completion.d
# * ~/bash-completion.d
#
# Documentation
# -------------
# The script is called by bash whenever [TAB] or [TAB][TAB] is pressed after
# 'filebot (..)'. By reading entered command line parameters,
# it determines possible bash completions and writes them to the COMPREPLY variable.
# Bash then completes the user input if only one entry is listed in the variable or
# shows the options if more than one is listed in COMPREPLY.
#
# References
# ----------
# [1] http://stackoverflow.com/a/12495480/1440785
# [2] http://tiswww.case.edu/php/chet/bash/FAQ
# [3] https://www.gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html
# [4] http://zsh.sourceforge.net/Doc/Release/Options.html#index-COMPLETE_005fALIASES
# [5] https://stackoverflow.com/questions/17042057/bash-check-element-in-array-for-elements-in-another-array/17042655#17042655
# [6] https://www.gnu.org/software/bash/manual/html_node/Programmable-Completion.html#Programmable-Completion
#
if [ -n "$BASH_VERSION" ]; then
# Enable programmable completion facilities when using bash (see [3])
shopt -s progcomp
elif [ -n "$ZSH_VERSION" ]; then
# Make alias a distinct command for completion purposes when using zsh (see [4])
setopt COMPLETE_ALIASES
alias compopt=complete
fi
# ArrContains takes two arguments, both of which are the name of arrays.
# It creates a temporary hash from lArr1 and then checks if all elements of lArr2
# are in the hashtable.
#
# Returns zero (no error) if all elements of the 2nd array are in the 1st array,
# otherwise returns 1 (error).
#
# Modified from [5]
function ArrContains() {
local lArr1 lArr2
declare -A tmp
eval lArr1=("\"\${$1[@]}\"")
eval lArr2=("\"\${$2[@]}\"")
for i in "${lArr1[@]}";{ [ -n "$i" ] && ((++tmp[$i]));}
for i in "${lArr2[@]}";{ [ -n "$i" ] && [ -z "${tmp[$i]}" ] && return 1;}
return 0
}
# Bash completion entry point function.
# _complete_filebot finds which commands and subcommands have been specified
# on the command line and delegates to the appropriate function
# to generate possible options and subcommands for the last specified subcommand.
function _complete_filebot() {
# No subcommands were specified; generate completions for the top-level command.
_picocli_filebot; return $?;
}
# Generates completions for the options and subcommands of the `filebot` command.
function _picocli_filebot() {
# Get completion data
CURR_WORD=${COMP_WORDS[COMP_CWORD]}
PREV_WORD=${COMP_WORDS[COMP_CWORD-1]}
COMMANDS=""
FLAG_OPTS="-rename -non-strict -get-subtitles -check -list -mediainfo -revert -extract -r -unixfs -no-xattr -no-history -clear-cache -clear-prefs -version -help"
ARG_OPTS="--mode --db --order --action --conflict --filter --mapper --format --q --lang --output --encoding -script --def --file-filter -exec --log --log-file --license"
mode_OPTION_ARGS="interactive" # --mode values
db_OPTION_ARGS="TheTVDB AniDB TheMovieDB::TV TVmaze TheMovieDB OMDb AcoustID ID3 exif xattr file" # --db values
order_OPTION_ARGS="Airdate DVD Absolute AbsoluteAirdate" # --order values
action_OPTION_ARGS="move copy keeplink symlink hardlink clone duplicate test" # --action values
conflict_OPTION_ARGS="skip override fail auto index" # --conflict values
lang_OPTION_ARGS="Albanian Arabic Armenian Bulgarian Catalan Croatian Czech Danish Dutch English Finnish French Canadian French German Greek Hebrew Hindi Hungarian Icelandic Indonesian Italian Japanese Romanized Japanese Korean Latvian Lithuanian Macedonian Malay Chinese Taiwanese Chinese Cantonese Norwegian Persian Polish Portuguese Brazilian Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Mexican Spanish Swedish Thai Turkish Ukrainian Vietnamese" # --lang values
encoding_OPTION_ARGS="UTF-8 Windows-1252 ISO-8859-1" # --encoding values
log_OPTION_ARGS="OFF SEVERE WARNING INFO CONFIG FINE FINER FINEST ALL" # --log values
compopt +o default
case ${PREV_WORD} in
--mode)
COMPREPLY=( $( compgen -W "${mode_OPTION_ARGS}" -- ${CURR_WORD} ) )
return $?
;;
--db)
COMPREPLY=( $( compgen -W "${db_OPTION_ARGS}" -- ${CURR_WORD} ) )
return $?
;;
--order)
COMPREPLY=( $( compgen -W "${order_OPTION_ARGS}" -- ${CURR_WORD} ) )
return $?
;;
--action)
COMPREPLY=( $( compgen -W "${action_OPTION_ARGS}" -- ${CURR_WORD} ) )
return $?
;;
--conflict)
COMPREPLY=( $( compgen -W "${conflict_OPTION_ARGS}" -- ${CURR_WORD} ) )
return $?
;;
--filter)
compopt -o filenames
COMPREPLY=( $( compgen -f -- ${CURR_WORD} ) ) # files
return $?
;;
--mapper)
compopt -o filenames
COMPREPLY=( $( compgen -f -- ${CURR_WORD} ) ) # files
return $?
;;
--format)
compopt -o filenames
COMPREPLY=( $( compgen -f -- ${CURR_WORD} ) ) # files
return $?
;;
--q)
return
;;
--lang)
COMPREPLY=( $( compgen -W "${lang_OPTION_ARGS}" -- ${CURR_WORD} ) )
return $?
;;
--output)
compopt -o filenames
COMPREPLY=( $( compgen -f -- ${CURR_WORD} ) ) # files
return $?
;;
--encoding)
COMPREPLY=( $( compgen -W "${encoding_OPTION_ARGS}" -- ${CURR_WORD} ) )
return $?
;;
-script)
compopt -o filenames
COMPREPLY=( $( compgen -f -- ${CURR_WORD} ) ) # files
return $?
;;
--def)
return
;;
--file-filter)
compopt -o filenames
COMPREPLY=( $( compgen -f -- ${CURR_WORD} ) ) # files
return $?
;;
-exec)
return
;;
--log)
COMPREPLY=( $( compgen -W "${log_OPTION_ARGS}" -- ${CURR_WORD} ) )
return $?
;;
--log-file)
compopt -o filenames
COMPREPLY=( $( compgen -f -- ${CURR_WORD} ) ) # files
return $?
;;
--license)
compopt -o filenames
COMPREPLY=( $( compgen -f -- ${CURR_WORD} ) ) # files
return $?
;;
esac
if [[ "${CURR_WORD}" == -* ]]; then
COMPREPLY=( $(compgen -W "${FLAG_OPTS} ${ARG_OPTS}" -- ${CURR_WORD}) )
else
COMPREPLY=( $(compgen -W "${COMMANDS}" -- ${CURR_WORD}) )
fi
}
# Define a completion specification (a compspec) for the
# `filebot`, `filebot.sh`, and `filebot.bash` commands.
# Uses the bash `complete` builtin (see [6]) to specify that shell function
# `_complete_filebot` is responsible for generating possible completions for the
# current word on the command line.
# The `-o default` option means that if the function generated no matches, the
# default Bash completions and the Readline default filename completions are performed.
complete -F _complete_filebot -o default filebot filebot.sh filebot.bash

View File

@ -0,0 +1,202 @@
package net.filebot.platform.bash;
import static net.filebot.WebServices.*;
import java.io.File;
import java.lang.reflect.Field;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.stream.Stream;
import net.filebot.Language;
import net.filebot.StandardRenameAction;
import net.filebot.cli.ConflictAction;
import net.filebot.web.Datasource;
import net.filebot.web.SortOrder;
import picocli.AutoComplete;
import picocli.CommandLine.Option;
import picocli.CommandLine.Parameters;
class BashCompletionBuilder {
@Option(names = "--mode", description = "Enable CLI interactive mode", completionCandidates = ModeCompletionCandidates.class)
public String mode;
@Option(names = "-rename", description = "Rename media files")
public boolean rename = false;
@Option(names = "--db", description = "Database", completionCandidates = DatabaseCompletionCandidates.class)
public String db;
@Option(names = "--order", description = "Episode order", completionCandidates = SortOrderCompletionCandidates.class)
public SortOrder order = SortOrder.Airdate;
@Option(names = "--action", description = "Rename action", completionCandidates = RenameActionCompletionCandidates.class)
public StandardRenameAction action = StandardRenameAction.MOVE;
@Option(names = "--conflict", description = "Conflict resolution", completionCandidates = ConflictActionCompletionCandidates.class)
public ConflictAction conflict = ConflictAction.SKIP;
@Option(names = "--filter", description = "Filter expression")
public File filter;
@Option(names = "--mapper", description = "Mapper expression")
public File mapper;
@Option(names = "--format", description = "Format expression")
public File format;
@Option(names = "-non-strict", description = "Enable advanced matching and more aggressive guessing")
public boolean nonStrict = false;
@Option(names = "-get-subtitles", description = "Fetch subtitles")
public boolean getSubtitles = false;
@Option(names = "--q", description = "Force lookup query")
public String query;
@Option(names = "--lang", description = "Language", completionCandidates = LanguageCompletionCandidates.class)
public String lang = "en";
@Option(names = "-check", description = "Create / Check verification files")
public boolean check = false;
@Option(names = "--output", description = "Output path")
public File output;
@Option(names = "--encoding", description = "Output character encoding", completionCandidates = CharsetCompletionCandidates.class)
public Charset encoding;
@Option(names = "-list", description = "Print episode list")
public boolean list = false;
@Option(names = "-mediainfo", description = "Print media info")
public boolean mediaInfo = false;
@Option(names = "-revert", description = "Revert files")
public boolean revert = false;
@Option(names = "-extract", description = "Extract archives")
public boolean extract = false;
@Option(names = "-script", description = "Run Groovy script")
public File script;
@Option(names = "--def", description = "Define script variables")
public Map<String, String> defines;
@Option(names = "-r", description = "Recursively process folders")
public boolean recursive = false;
@Option(names = "--file-filter", description = "Input file filter expression")
public File inputFileFilter;
@Option(names = "-exec", arity = "1..*", description = "Execute command")
public List<String> exec;
@Option(names = "-unixfs", description = "Allow special characters in file paths")
public boolean unixfs = false;
@Option(names = "-no-xattr", description = "Disable extended attributes")
public boolean disableExtendedAttributes = false;
@Option(names = "-no-history", description = "Disable history")
public boolean disableHistory = false;
@Option(names = "--log", description = "Log level", completionCandidates = LogLevelCompletionCandidates.class)
public String log = "all";
@Option(names = "--log-file", description = "Log file")
public File logFile;
@Option(names = "-clear-cache", description = "Clear cached and temporary data")
public boolean clearCache = false;
@Option(names = "-clear-prefs", description = "Clear application settings")
public boolean clearPrefs = false;
@Option(names = "-version", description = "Print version identifier")
public boolean version = false;
@Option(names = "-help", description = "Print this help message")
public boolean help = false;
@Option(names = "--license", description = "Import license file", paramLabel = "*.psm")
public File license;
@Parameters
public List<String> arguments = new ArrayList<String>();
private static class DatabaseCompletionCandidates implements Iterable<String> {
@Override
public Iterator<String> iterator() {
return Stream.of(getEpisodeListProviders(), getMovieIdentificationServices(), getMusicIdentificationServices(), getLocalDatasources()).flatMap(Stream::of).map(Datasource::getIdentifier).iterator();
}
}
private static class SortOrderCompletionCandidates implements Iterable<String> {
@Override
public Iterator<String> iterator() {
return Stream.of(SortOrder.values()).map(Enum::name).iterator();
}
}
private static class RenameActionCompletionCandidates implements Iterable<String> {
@Override
public Iterator<String> iterator() {
return Stream.of(StandardRenameAction.values()).map(Enum::name).map(String::toLowerCase).iterator();
}
}
private static class ConflictActionCompletionCandidates implements Iterable<String> {
@Override
public Iterator<String> iterator() {
return Stream.of(ConflictAction.values()).map(Enum::name).map(String::toLowerCase).iterator();
}
}
private static class ModeCompletionCandidates implements Iterable<String> {
@Override
public Iterator<String> iterator() {
return Stream.of("interactive").iterator();
}
}
private static class LanguageCompletionCandidates implements Iterable<String> {
@Override
public Iterator<String> iterator() {
return Language.availableLanguages().stream().map(Language::getName).iterator();
}
}
private static class LogLevelCompletionCandidates implements Iterable<String> {
@Override
public Iterator<String> iterator() {
return Stream.of(Level.class.getFields()).map(Field::getName).iterator();
}
}
private static class CharsetCompletionCandidates implements Iterable<String> {
@Override
public Iterator<String> iterator() {
return Stream.of("UTF-8", "Windows-1252", "ISO-8859-1").iterator();
}
}
public static void main(String[] args) {
AutoComplete.main("-n", "filebot", BashCompletionBuilder.class.getName());
}
}