Commit | Line | Data |
---|---|---|
8a4fc9ec SBS |
1 | #!/usr/bin/env bash |
2 | # Desc: Removes lines that aren't audio file paths | |
3 | # Usage: find . -type f | isAudio | |
4 | # Input: stdin | |
5 | # Output: stdout | |
6 | # Version: 0.0.1 | |
7 | # Example: find ~/Music/ -type f | isAudio | mpv --playlist=- | |
8 | # Depends: | |
9 | # BK-2020-03: read_stdin() 0.0.1 | |
10 | ||
11 | declare -a music_codecs # Array for storing valid codec names (e.g. "aac" "mp3") | |
12 | ||
13 | # Adjustable parameters | |
14 | music_codecs=("vorbis" "aac" "mp3" "flac" "opus"); # whitelist of valid codec_names ffprobe might return | |
15 | ||
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 | |
19 | read_stdin() { | |
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) | |
25 | # Version: 0.0.1 | |
26 | local input_stdin output; | |
27 | ||
28 | # Store stdin | |
29 | if [[ -p /dev/stdin ]]; then | |
30 | input_stdin="$(cat -)"; | |
31 | fi; | |
32 | ||
33 | # Store as output array elements | |
34 | ## Read in stdin | |
35 | if [[ -n $input_stdin ]]; then | |
36 | while read -r line; do | |
37 | output+=("$line"); | |
38 | done < <(printf "%s\n" "$input_stdin"); | |
39 | fi; | |
40 | ||
41 | # Print to stdout | |
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] | |
47 | # Version: 0.0.1 | |
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 | |
52 | ||
53 | if [[ $# -ne 1 ]]; then die "ERROR:Invalid number of args:$#"; fi; | |
54 | ||
55 | file_in="$1"; | |
56 | ||
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 | |
59 | return_state="true"; | |
60 | else | |
61 | return_state="false"; | |
62 | fi; | |
63 | ||
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 | |
67 | return_state="false"; | |
68 | fi; | |
69 | ||
70 | # Report exit code | |
71 | if [[ $return_state = "true" ]]; then | |
72 | return 0; | |
73 | else | |
74 | return 1; | |
75 | fi; | |
76 | } # Checks if file has valid codec name using ffprobe | |
77 | get_audio_format() { | |
78 | # Desc: Gets audio format of file as string | |
79 | # Usage: get_audio_format arg1 | |
80 | # Depends: ffprobe | |
81 | # Version: 0.0.1 | |
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; | |
91 | local return_state; | |
92 | file_in="$1"; | |
93 | ||
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 | |
97 | return_state="false"; | |
98 | fi; | |
99 | ||
100 | # Get audio format | |
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] | |
102 | ||
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 | |
106 | return_state="true"; | |
107 | # Report audio format | |
108 | echo "$audio_format"; | |
109 | else | |
110 | return_state="false"; | |
111 | fi; | |
112 | ||
113 | # Report exit code | |
114 | if [[ $return_state = "true" ]]; then | |
115 | return 0; | |
116 | else | |
117 | return 1; | |
118 | fi; | |
119 | } # Get audio format as stdout | |
120 | checkIsInArray() { | |
121 | # Desc: Checks if input arg is element in array | |
122 | # Usage: checkIsInArray arg1 arg2 | |
123 | # Version: 0.0.1 | |
124 | # Input: arg1: test string | |
125 | # arg2: array | |
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] | |
133 | arg1="${input[0]}"; | |
134 | arg2=("${input[@]:1}"); | |
135 | #yell "DEBUG:input:${input[@]}"; | |
136 | #yell "DEBUG:arg1:${arg1[@]}"; | |
137 | #yell "DEBUG:arg2:${arg2[@]}"; | |
138 | ||
139 | string_test="$arg1"; | |
140 | array_test=("${arg2[@]}"); | |
141 | ||
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 | |
147 | return_state="true"; | |
148 | continue; | |
149 | fi; | |
150 | done; | |
151 | ||
152 | # Report exit code | |
153 | if [[ $return_state == "true" ]]; then | |
154 | return 0; | |
155 | else | |
156 | return 1; | |
157 | fi; | |
158 | } # Check if string is element in array | |
159 | check_depends() { | |
160 | if ! command -v ffprobe 1>/dev/random 2>&1; then | |
161 | die "FATAL:Missing ffprobe."; fi; | |
162 | }; | |
163 | main() { | |
164 | # Input: array: music_codecs ("mp3" "aac" ...) | |
165 | # stdin | |
166 | # Output: stdout | |
167 | # Depends: | |
168 | # BK-2020-03: read_stdin() 0.0.1 | |
169 | ||
170 | # check dependencies | |
171 | check_depends; | |
172 | ||
173 | # iterate through stdin lines | |
174 | while read -r line; do | |
175 | # check if file | |
176 | if [[ ! -f $line ]]; then continue; fi; # reject | |
177 | ||
178 | # check if valid codec | |
179 | if ! check_parsable_audio_ffprobe "$line"; then | |
180 | continue; fi; # reject | |
181 | ||
182 | # Check if desired codec | |
183 | file_format="$(get_audio_format "$line")"; | |
184 | if ! checkIsInArray "$file_format" "${music_codecs[@]}"; then | |
185 | continue; fi; # reject | |
186 | ||
187 | # Output line to stdout | |
188 | printf "%s\n" "$line"; | |
189 | done < <(read_stdin); | |
190 | }; # main program | |
191 | ||
192 | main "$@" 2>/dev/random; | |
193 | ||
194 | # Author: Steven Baltakatei Sandoval | |
195 | # License: GPLv3+ |