X-Git-Url: https://zdv2.bktei.com/gitweb/BK-2020-03.git/blobdiff_plain/75e9293975f0308899d964e9b266280c636e101b..df7774c1d8ac3cddfbf1f209a9ac437d82eabb35:/user/bkmpv2 diff --git a/user/bkmpv2 b/user/bkmpv2 index 19f605e..36453db 100755 --- a/user/bkmpv2 +++ b/user/bkmpv2 @@ -1,15 +1,17 @@ #!/usr/bin/env bash -# Desc: Wrapper for mpv that accepts directory paths via posargs or stdin lines +# Desc: Wrapper for mpv that accepts directory or file paths via posargs or stdin lines # Usage: bkmpv2 [DIR] -# Version: 0.0.1 +# Version: 0.2.1 # Depends: GNU Parallel, GNU Bash v5.1.16, mpv v0.34.1, bc v1.07.1 # Ref/Attrib: [1] Tange, Ole. GNU Parallel with Bash Array. 2019-03-24. https://unix.stackexchange.com/a/508365/411854 # Example: find $HOME/Music -type d | bkmpv2 # Example: bkmpv2 $HOME/Music/ +# Example: find $HOME -type f -name "*.mp3" | bkmpv2 # Note: Does not follow symlinks # Find settings -firegex=".+\(aac\|aif\|aiff\|flac\|m4a\|mp3\|mp4\|ogg\|opus\|wav\)$"; # update according to `find . -type f | grep -Eo "\.([[:alnum:]])+$" | sort -u` +firegex=".+\(aac\|aif\|aiff\|flac\|m4a\|mp3\|mp4\|ogg\|opus\|wav\)$"; # POSIX regex for find. Update according to `find . -type f | grep -Eo "\.([[:alnum:]])+$" | sort -u` +file_regex=".+(aac|aif|aiff|flac|m4a|mp3|mp4|ogg|opus|wav)$"; # extended regex for Bash. fsize="10k"; # default: minimum "10k" fdepth_posarg="10"; # find depth for positional arguments fdepth_stdin="1"; # find depth for stdin @@ -29,23 +31,23 @@ checkapp() { # Input: global assoc. array 'appRollCall' # Output: adds/updates key(value) to global assoc array 'appRollCall' # Depends: bash 5.0.3 - local returnState + local returnState #===Process Args=== for arg in "$@"; do - if command -v "$arg" 1>/dev/null 2>&1; then # Check if arg is a valid command - appRollCall[$arg]="true"; - if ! [ "$returnState" = "false" ]; then returnState="true"; fi; - else - appRollCall[$arg]="false"; returnState="false"; - fi; + if command -v "$arg" 1>/dev/null 2>&1; then # Check if arg is a valid command + appRollCall[$arg]="true"; + if ! [ "$returnState" = "false" ]; then returnState="true"; fi; + else + appRollCall[$arg]="false"; returnState="false"; + fi; done; #===Determine function return code=== if [ "$returnState" = "true" ]; then - return 0; + return 0; else - return 1; + return 1; fi; } # Check that app exists checkfile() { @@ -60,19 +62,19 @@ checkfile() { #===Process Args=== for arg in "$@"; do - if [ -f "$arg" ]; then - fileRollCall["$arg"]="true"; - if ! [ "$returnState" = "false" ]; then returnState="true"; fi; - else - fileRollCall["$arg"]="false"; returnState="false"; - fi; + if [ -f "$arg" ]; then + fileRollCall["$arg"]="true"; + if ! [ "$returnState" = "false" ]; then returnState="true"; fi; + else + fileRollCall["$arg"]="false"; returnState="false"; + fi; done; - + #===Determine function return code=== if [ "$returnState" = "true" ]; then - return 0; + return 0; else - return 1; + return 1; fi; } # Check that file exists checkdir() { @@ -87,19 +89,19 @@ checkdir() { #===Process Args=== for arg in "$@"; do - if [ -d "$arg" ]; then - dirRollCall["$arg"]="true"; - if ! [ "$returnState" = "false" ]; then returnState="true"; fi - else - dirRollCall["$arg"]="false"; returnState="false"; - fi + if [ -d "$arg" ]; then + dirRollCall["$arg"]="true"; + if ! [ "$returnState" = "false" ]; then returnState="true"; fi + else + dirRollCall["$arg"]="false"; returnState="false"; + fi done - + #===Determine function return code=== if [ "$returnState" = "true" ]; then - return 0; + return 0; else - return 1; + return 1; fi } # Check that dir exists displayMissing() { @@ -111,21 +113,21 @@ displayMissing() { # Depends: bash 5, checkAppFileDir() local missingApps value appMissing missingFiles fileMissing local missingDirs dirMissing - + #==BEGIN Display errors== #===BEGIN Display Missing Apps=== missingApps="Missing apps :"; #for key in "${!appRollCall[@]}"; do echo "DEBUG:$key => ${appRollCall[$key]}"; done for key in "${!appRollCall[@]}"; do - value="${appRollCall[$key]}"; - if [ "$value" = "false" ]; then - #echo "DEBUG:Missing apps: $key => $value"; - missingApps="$missingApps""$key "; - appMissing="true"; - fi; + value="${appRollCall[$key]}"; + if [ "$value" = "false" ]; then + #echo "DEBUG:Missing apps: $key => $value"; + missingApps="$missingApps""$key "; + appMissing="true"; + fi; done; if [ "$appMissing" = "true" ]; then # Only indicate if an app is missing. - echo "$missingApps" 1>&2; + echo "$missingApps" 1>&2; fi; unset value; #===END Display Missing Apps=== @@ -134,15 +136,15 @@ displayMissing() { missingFiles="Missing files:"; #for key in "${!fileRollCall[@]}"; do echo "DEBUG:$key => ${fileRollCall[$key]}"; done for key in "${!fileRollCall[@]}"; do - value="${fileRollCall[$key]}"; - if [ "$value" = "false" ]; then - #echo "DEBUG:Missing files: $key => $value"; - missingFiles="$missingFiles""$key "; - fileMissing="true"; - fi; + value="${fileRollCall[$key]}"; + if [ "$value" = "false" ]; then + #echo "DEBUG:Missing files: $key => $value"; + missingFiles="$missingFiles""$key "; + fileMissing="true"; + fi; done; if [ "$fileMissing" = "true" ]; then # Only indicate if an app is missing. - echo "$missingFiles" 1>&2; + echo "$missingFiles" 1>&2; fi; unset value; #===END Display Missing Files=== @@ -151,15 +153,15 @@ displayMissing() { missingDirs="Missing dirs:"; #for key in "${!dirRollCall[@]}"; do echo "DEBUG:$key => ${dirRollCall[$key]}"; done for key in "${!dirRollCall[@]}"; do - value="${dirRollCall[$key]}"; - if [ "$value" = "false" ]; then - #echo "DEBUG:Missing dirs: $key => $value"; - missingDirs="$missingDirs""$key "; - dirMissing="true"; - fi; + value="${dirRollCall[$key]}"; + if [ "$value" = "false" ]; then + #echo "DEBUG:Missing dirs: $key => $value"; + missingDirs="$missingDirs""$key "; + dirMissing="true"; + fi; done; if [ "$dirMissing" = "true" ]; then # Only indicate if an dir is missing. - echo "$missingDirs" 1>&2; + echo "$missingDirs" 1>&2; fi; unset value; #===END Display Missing Directories=== @@ -185,21 +187,21 @@ checkInt() { #===Process Arg=== if [[ $# -ne 1 ]]; then - die "ERROR:Invalid number of arguments:$#"; + die "ERROR:Invalid number of arguments:$#"; fi; - + RETEST1='^[0-9]+$'; # Regular Expression to test if [[ ! $1 =~ $RETEST1 ]] ; then - returnState="false"; + returnState="false"; else - returnState="true"; + returnState="true"; fi; #===Determine function return code=== if [ "$returnState" = "true" ]; then - return 0; + return 0; else - return 1; + return 1; fi; } # Checks if arg is integer read_stdin() { @@ -214,8 +216,8 @@ read_stdin() { # Store stdin if [[ -p /dev/stdin ]]; then input_stdin="$(cat -)"; - fi; - + fi; + # Store as output array elements ## Read in stdin if [[ -n $input_stdin ]]; then @@ -235,12 +237,12 @@ read_psarg() { # Depends: GNU bash (version 5.1.16) # Version: 0.0.1 local input_psarg output; - + # Store arguments if [[ $# -gt 0 ]]; then input_psarg="$*"; fi; - + # Store as output array elements ## Read in positional arguments if [[ -n $input_psarg ]]; then @@ -261,6 +263,21 @@ find_flist() { if [[ ! -d "$1" ]]; then return 1; fi; must find "$1" -maxdepth "$fdepth" -type f -iregex "$firegex" -size +"$fsize"; }; # print file list to stdout from dir with script parameters +check_files() { + # Desc: Applies $file_regex to files specified by path + # Input: var: file_regex + # array: files_stdin + # Output: array: files_stdin + local file; + declare -a filtered_files_stdin; + + for file in "${files_stdin[@]}"; do + if [[ "$file" =~ $file_regex ]]; then + filtered_files_stdin+=("$file"); + fi; + done; + files_stdin=("${filtered_files_stdin[@]}"); +}; # apply $firegex to files_stdin array main() { # Input: var: firegex find iregex file name pattern # var: fsize find minimum file siz @@ -268,7 +285,7 @@ main() { # var: fc_redbase logarithm base to reduce output file count local re_dotfile; - declare -a dirs_stdin dirs_psarg; + declare -a files_stdin dirs_stdin dirs_psarg; declare -a paths_files; declare list_paths_files; declare -a cmd_args; @@ -280,45 +297,40 @@ main() { re_dotfile="^\."; # first char is a dot while read -r line; do line="$(readlink -e "$line")"; - # Check if dir - if [[ ! -d "$line" ]]; then - echo "ERROR:Not a dir:$line" 1>&2; + line_bn="$(basename "$line")"; + # Check if dir and not dotfile + if [[ -d "$line" ]] && [[ ! "$line_bn" =~ $re_dotfile ]]; then + dirs_stdin+=("$line"); continue; fi; - dir_name="$(basename "$line")"; - # Exclude dotdirs - if [[ "$dir_name" =~ $re_dotfile ]]; then - echo "ERROR:Is a dotdir:$line" 1>&2; - continue - fi; - dirs_stdin+=("$line"); - done < <(read_stdin); - yell "STATUS:$SECONDS:Read stdin."; - ## Read positional arguments as lines - re_dotfile="^\."; # first char is a dot - while read -r line; do - line="$(readlink -e "$line")"; - # Check if dir - if [[ ! -d "$line" ]]; then - echo "ERROR:Not a dir:$line" 1>&2; + + # Check if file + if [[ -f "$line" ]]; then + files_stdin+=("$line"); continue; fi; - dir_name="$(basename "$line")"; - # Exclude dotdirs - if [[ "$dir_name" =~ $re_dotfile ]]; then - echo "ERROR:Is a dotdir:$line" 1>&2; - continue - fi; - dirs_psarg+=("$line"); - done < <(read_psarg "$@"); - yell "STATUS:$SECONDS:Read posargs."; - + + # Throw warning + yell "WARNING:Not a valid dir or file:$line"; + done < <( read_stdin; read_psarg "$@"; ); + yell "STATUS:$SECONDS:Read stdin and psargs."; + + # Apply the $file_regex to $files_stdin array + check_files; + # Catch all arrays empty - if [[ "${#dirs_stdin[@]}" -le 0 ]] && [[ "${#dirs_psarg[@]}" -le 0 ]]; then - die "FATAL:No valid directories provided."; + if [[ "${#dirs_stdin[@]}" -le 0 ]] && \ + [[ "${#dirs_psarg[@]}" -le 0 ]] && \ + [[ "${#files_stdin[@]}" -le 0 ]]; then + die "FATAL:No valid directories or files provided."; fi; # Generate file list + ## Add stdin argument input + if [[ "${#files_stdin[@]}" -gt 0 ]]; then + paths_files+=("${files_stdin[@]}"); + fi; + ## Call find_filelist() in parallel for positional argument input if [[ "${#dirs_psarg[@]}" -gt 0 ]]; then fdepth="$fdepth_posarg"; export fdepth; # for dirs from positional arguments @@ -354,7 +366,7 @@ main() { bc_exp="$fc_falloff * (1 + l( $fc / $fc_falloff )/l($fc_redbase))"; fc_out="$( echo "$bc_exp" | bc -l )"; else - fc_out="$fc"; + fc_out="$fc"; fi; ## Reduce output file count by fixed fraction (bkshuf optimization) fc_out="$(echo "$fc_out * 0.75" | bc -l)"; @@ -365,7 +377,7 @@ main() { ### Round file count down fc_out="$(printf "%.0f" "$fc_out")"; # round float down to int ### Get neighbor-preserving shuffled subset (size $fc_out) - yell "STATUS:$SECONDS:Selecting $fc_out files via bkshuf..."; + yell "STATUS:$SECONDS:Selecting $fc_out of $fc files via bkshuf..."; must \ echo -n "$list_paths_files" | \ bkshuf "$fc_out" > "$list_paths_files_tmp"; @@ -380,7 +392,6 @@ main() { cmd_args+=("--audio-display=no"); # disable video for audio files cmd_args+=("--vid=no"); # donʼt show video track cmd_args+=("--image-display-duration=0"); # don't show album art - cmd_args+=("--af=scaletempo=stride=15:overlap=1:search=15"); # improve scrubbing cmd_args+=("--playlist=$list_paths_files_tmp"); # read playlist declare -p cmd_args; # debug ## Execute command