feat(user/bkmpv2): v0.1.0: Permit specifying files as well as dirs
[BK-2020-03.git] / user / bkmpv2
index 19f605e2ebf90cfcf4d860b65c71d984542c01c3..d44fff7ef72134b08d0176a0edb40598e6d94844 100755 (executable)
@@ -1,11 +1,12 @@
 #!/usr/bin/env bash
 #!/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]
 # Usage: bkmpv2 [DIR]
-# Version: 0.0.1
+# Version: 0.1.0
 # 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/
 # 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
 # Note: Does not follow symlinks
 
 # Find settings
@@ -29,23 +30,23 @@ checkapp() {
     # Input: global assoc. array 'appRollCall'
     # Output: adds/updates key(value) to global assoc array 'appRollCall'
     # Depends: bash 5.0.3
     # 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
 
     #===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
     done;
 
     #===Determine function return code===
     if [ "$returnState" = "true" ]; then
-       return 0;
+        return 0;
     else
     else
-       return 1;
+        return 1;
     fi;
 } # Check that app exists
 checkfile() {
     fi;
 } # Check that app exists
 checkfile() {
@@ -60,19 +61,19 @@ checkfile() {
 
     #===Process Args===
     for arg in "$@"; do
 
     #===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;
     done;
-    
+
     #===Determine function return code===
     if [ "$returnState" = "true" ]; then
     #===Determine function return code===
     if [ "$returnState" = "true" ]; then
-       return 0;
+        return 0;
     else
     else
-       return 1;
+        return 1;
     fi;
 } # Check that file exists
 checkdir() {
     fi;
 } # Check that file exists
 checkdir() {
@@ -87,19 +88,19 @@ checkdir() {
 
     #===Process Args===
     for arg in "$@"; do
 
     #===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
     done
-    
+
     #===Determine function return code===
     if [ "$returnState" = "true" ]; then
     #===Determine function return code===
     if [ "$returnState" = "true" ]; then
-       return 0;
+        return 0;
     else
     else
-       return 1;
+        return 1;
     fi
 } # Check that dir exists
 displayMissing() {
     fi
 } # Check that dir exists
 displayMissing() {
@@ -111,21 +112,21 @@ displayMissing() {
     # Depends: bash 5, checkAppFileDir()
     local missingApps value appMissing missingFiles fileMissing
     local missingDirs dirMissing
     # 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
     #==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.
     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===
     fi;
     unset value;
     #===END Display Missing Apps===
@@ -134,15 +135,15 @@ displayMissing() {
     missingFiles="Missing files:";
     #for key in "${!fileRollCall[@]}"; do echo "DEBUG:$key => ${fileRollCall[$key]}"; done
     for key in "${!fileRollCall[@]}"; do
     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.
     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===
     fi;
     unset value;
     #===END Display Missing Files===
@@ -151,15 +152,15 @@ displayMissing() {
     missingDirs="Missing dirs:";
     #for key in "${!dirRollCall[@]}"; do echo "DEBUG:$key => ${dirRollCall[$key]}"; done
     for key in "${!dirRollCall[@]}"; do
     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.
     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===
     fi;
     unset value;
     #===END Display Missing Directories===
@@ -185,21 +186,21 @@ checkInt() {
 
     #===Process Arg===
     if [[ $# -ne 1 ]]; then
 
     #===Process Arg===
     if [[ $# -ne 1 ]]; then
-       die "ERROR:Invalid number of arguments:$#";
+        die "ERROR:Invalid number of arguments:$#";
     fi;
     fi;
-    
+
     RETEST1='^[0-9]+$'; # Regular Expression to test
     if [[ ! $1 =~ $RETEST1 ]] ; then
     RETEST1='^[0-9]+$'; # Regular Expression to test
     if [[ ! $1 =~ $RETEST1 ]] ; then
-       returnState="false";
+        returnState="false";
     else
     else
-       returnState="true";
+        returnState="true";
     fi;
 
     #===Determine function return code===
     if [ "$returnState" = "true" ]; then
     fi;
 
     #===Determine function return code===
     if [ "$returnState" = "true" ]; then
-       return 0;
+        return 0;
     else
     else
-       return 1;
+        return 1;
     fi;
 } # Checks if arg is integer
 read_stdin() {
     fi;
 } # Checks if arg is integer
 read_stdin() {
@@ -214,8 +215,8 @@ read_stdin() {
     # Store stdin
     if [[ -p /dev/stdin ]]; then
         input_stdin="$(cat -)";
     # 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
     # Store as output array elements
     ## Read in stdin
     if [[ -n $input_stdin ]]; then
@@ -235,12 +236,12 @@ read_psarg() {
     # Depends: GNU bash (version 5.1.16)
     # Version: 0.0.1
     local input_psarg output;
     # 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 arguments
     if [[ $# -gt 0 ]]; then
         input_psarg="$*";
     fi;
-    
+
     # Store as output array elements
     ## Read in positional arguments
     if [[ -n $input_psarg ]]; then
     # Store as output array elements
     ## Read in positional arguments
     if [[ -n $input_psarg ]]; then
@@ -268,7 +269,7 @@ main() {
     #        var: fc_redbase  logarithm base to reduce output file count
 
     local re_dotfile;
     #        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;
     declare -a paths_files;
     declare list_paths_files;
     declare -a cmd_args;
@@ -280,45 +281,37 @@ main() {
     re_dotfile="^\."; # first char is a dot
     while read -r line; do
         line="$(readlink -e "$line")";
     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;
             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;
             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.";
+
     # Catch all arrays empty
     # 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
     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
     ## 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 +347,7 @@ main() {
             bc_exp="$fc_falloff * (1 + l( $fc / $fc_falloff )/l($fc_redbase))";
             fc_out="$( echo "$bc_exp" | bc -l )";
     else
             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)";
     fi;
     ## Reduce output file count by fixed fraction (bkshuf optimization)
     fc_out="$(echo "$fc_out * 0.75" | bc -l)";
@@ -365,7 +358,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)
     ### 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";
     must \
         echo -n "$list_paths_files" | \
         bkshuf "$fc_out" > "$list_paths_files_tmp";