2 # Desc: Removes lines that aren't audio file paths 
   3 # Usage: find . -type f | isAudio 
   7 # Example: find ~/Music/ -type f | isAudio | mpv --playlist=- 
   9 #   BK-2020-03: read_stdin() 0.0.1 
  11 declare -a music_codecs 
# Array for storing valid codec names (e.g. "aac" "mp3") 
  13 # Adjustable parameters 
  14 music_codecs
=("vorbis" "aac" "mp3" "flac" "opus"); # whitelist of valid codec_names ffprobe might return 
  16 yell
() { echo "$0: $*" >&2; } # print script path and all args to stderr 
  17 die
() { yell 
"$*"; exit 111; } # same as yell() but non-zero exit status 
  18 must
() { "$@" || die 
"cannot $*"; } # runs args as command, reports args if command fails 
  20     # Desc: Consumes stdin; outputs as stdout lines 
  21     # Input: stdin (consumes) 
  22     # Output: stdout (newline delimited) 
  23     # Example: printf "foo\nbar\n" | read_stdin 
  24     # Depends: GNU bash (version 5.1.16) 
  26     local input_stdin output
; 
  29     if [[ -p /dev
/stdin 
]]; then 
  30         input_stdin
="$(cat -)"; 
  33     # Store as output array elements 
  35     if [[ -n $input_stdin ]]; then 
  36         while read -r line
; do 
  38         done < <(printf "%s\n" "$input_stdin"); 
  42     printf "%s\n" "${output[@]}"; 
  43 }; # read stdin to stdout lines 
  44 check_parsable_audio_ffprobe
() { 
  45     # Desc: Checks if ffprobe returns valid audio codec name for file 
  46     # Usage: check_parsable_audio_ffprobe [path FILE] 
  48     # Input: arg1: file path 
  49     # Output: exit code 0 if returns valid codec name; 1 otherwise 
  50     # Depends: ffprobe, die() 
  51     local file_in ffprobe_out
 
  53     if [[ $# -ne 1 ]]; then die 
"ERROR:Invalid number of args:$#"; fi; 
  57     # Check if ffprobe detects an audio stream 
  58     if ffprobe 
-v error 
-select_streams a 
-show_entries stream
=codec_name 
-of default
=nokey
=1:noprint_wrappers
=1 "$file_in" 1>/dev
/null 
2>&1; then 
  64     # Fail if ffprobe returns no result 
  65     ffprobe_out
="$(ffprobe -v error -select_streams a -show_entries stream=codec_name -of default=nokey=1:noprint_wrappers=1 "$file_in")"; 
  66     if [[ -z $ffprobe_out ]]; then 
  71     if [[ $return_state = "true" ]]; then 
  76 } # Checks if file has valid codec name using ffprobe 
  78     # Desc: Gets audio format of file as string 
  79     # Usage: get_audio_format arg1 
  82     # Input: arg1: input file path 
  83     # Output: stdout (if valid audio format) 
  84     #         exit code 0 if audio file; 1 otherwise 
  85     # Example: get_audio_format myvideo.mp4 
  86     #   Note: Would return "opus" if full ffprobe report had 'Audio: opus, 48000 Hz, stereo, fltp' 
  87     # Note: Not tested with videos containing multiple video streams 
  88     # Ref/Attrib: [1] https://stackoverflow.com/questions/5618363/is-there-a-way-to-use-ffmpeg-to-determine-the-encoding-of-a-file-before-transcod 
  89     #             [2] https://stackoverflow.com/questions/44123532/how-to-find-out-the-file-extension-for-extracting-audio-tracks-with-ffmpeg-and-p#comment88464070_50723126 
  90     local audio_format file_in
; 
  94     # Return error exit code if not audio file 
  95     ## Return error if ffprobe itself exited on error 
  96     if ! ffprobe 
-v error 
-select_streams a 
-show_entries stream
=codec_name 
-of default
=nokey
=1:noprint_wrappers
=1 "$file_in" 1>/dev
/null 
2>&1; then 
 101     audio_format
="$(ffprobe -v error -select_streams a -show_entries stream=codec_name -of default=nokey=1:noprint_wrappers=1 "$file_in")"; # see [1] 
 103     ## Return error if audio format is incorrectly formatted (e.g. reject if contains spaces) 
 104     pattern
="^[[:alnum:]]+$"; # alphanumeric string with no spaces 
 105     if [[ $audio_format =~ 
$pattern ]]; then 
 107         # Report audio format 
 108         echo "$audio_format"; 
 110         return_state
="false"; 
 114     if [[ $return_state = "true" ]]; then 
 119 } # Get audio format as stdout 
 121     # Desc: Checks if input arg is element in array 
 122     # Usage: checkIsInArray arg1 arg2 
 124     # Input: arg1: test string 
 126     # Output: exit code 0 if test string is in array; 1 otherwise 
 127     # Example: checkIsInArray "foo" "${myArray[@]}" 
 128     # Ref/Attrib: [1] How do I check if variable is an array? https://stackoverflow.com/a/27254437 
 129     #             [2] How to pass an array as function argument? https://askubuntu.com/a/674347 
 130     local return_state input arg1 string_test
 
 131     declare -a arg2 array_test
 
 132     input
=("$@") # See [2] 
 134     arg2
=("${input[@]:1}"); 
 135     #yell "DEBUG:input:${input[@]}"; 
 136     #yell "DEBUG:arg1:${arg1[@]}"; 
 137     #yell "DEBUG:arg2:${arg2[@]}"; 
 140     array_test
=("${arg2[@]}"); 
 142     #yell "DEBUG:string_test:$string_test"; 
 143     #yell "DEBUG:$(declare -p array_test)"; 
 144     for element 
in "${array_test[@]}"; do 
 145         #yell "DEBUG:element:$element"; 
 146         if [[ "$element" =~ ^
"$string_test" ]]; then 
 153     if [[ $return_state == "true" ]]; then 
 158 } # Check if string is element in array 
 160     if ! command -v ffprobe 
1>/dev
/random 
2>&1; then 
 161         die 
"FATAL:Missing ffprobe."; fi; 
 164     # Input: array: music_codecs ("mp3" "aac" ...) 
 168     #   BK-2020-03: read_stdin() 0.0.1 
 173     # iterate through stdin lines 
 174     while read -r line
; do 
 176         if [[ ! -f $line ]]; then continue; fi; # reject 
 178         # check if valid codec 
 179         if ! check_parsable_audio_ffprobe 
"$line"; then 
 180             continue; fi; # reject 
 182         # Check if desired codec 
 183         file_format
="$(get_audio_format "$line")"; 
 184         if ! checkIsInArray 
"$file_format" "${music_codecs[@]}"; then 
 185             continue; fi; # reject 
 187         # Output line to stdout 
 188         printf "%s\n" "$line"; 
 189     done < <(read_stdin
); 
 192 main 
"$@" 2>/dev
/random
; 
 194 # Author: Steven Baltakatei Sandoval