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