+#!/usr/bin/env bash
+# Desc: Removes lines that aren't audio file paths
+# Usage: find . -type f | isAudio
+# Input: stdin
+# Output: stdout
+# Version: 0.0.1
+# Example: find ~/Music/ -type f | isAudio | mpv --playlist=-
+# Depends:
+# BK-2020-03: read_stdin() 0.0.1
+
+declare -a music_codecs # Array for storing valid codec names (e.g. "aac" "mp3")
+
+# Adjustable parameters
+music_codecs=("vorbis" "aac" "mp3" "flac" "opus"); # whitelist of valid codec_names ffprobe might return
+
+yell() { echo "$0: $*" >&2; } # print script path and all args to stderr
+die() { yell "$*"; exit 111; } # same as yell() but non-zero exit status
+must() { "$@" || die "cannot $*"; } # runs args as command, reports args if command fails
+read_stdin() {
+ # Desc: Consumes stdin; outputs as stdout lines
+ # Input: stdin (consumes)
+ # Output: stdout (newline delimited)
+ # Example: printf "foo\nbar\n" | read_stdin
+ # Depends: GNU bash (version 5.1.16)
+ # Version: 0.0.1
+ local input_stdin output;
+
+ # Store stdin
+ if [[ -p /dev/stdin ]]; then
+ input_stdin="$(cat -)";
+ fi;
+
+ # Store as output array elements
+ ## Read in stdin
+ if [[ -n $input_stdin ]]; then
+ while read -r line; do
+ output+=("$line");
+ done < <(printf "%s\n" "$input_stdin");
+ fi;
+
+ # Print to stdout
+ printf "%s\n" "${output[@]}";
+}; # read stdin to stdout lines
+check_parsable_audio_ffprobe() {
+ # Desc: Checks if ffprobe returns valid audio codec name for file
+ # Usage: check_parsable_audio_ffprobe [path FILE]
+ # Version: 0.0.1
+ # Input: arg1: file path
+ # Output: exit code 0 if returns valid codec name; 1 otherwise
+ # Depends: ffprobe, die()
+ local file_in ffprobe_out
+
+ if [[ $# -ne 1 ]]; then die "ERROR:Invalid number of args:$#"; fi;
+
+ file_in="$1";
+
+ # Check if ffprobe detects an audio stream
+ 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
+ return_state="true";
+ else
+ return_state="false";
+ fi;
+
+ # Fail if ffprobe returns no result
+ ffprobe_out="$(ffprobe -v error -select_streams a -show_entries stream=codec_name -of default=nokey=1:noprint_wrappers=1 "$file_in")";
+ if [[ -z $ffprobe_out ]]; then
+ return_state="false";
+ fi;
+
+ # Report exit code
+ if [[ $return_state = "true" ]]; then
+ return 0;
+ else
+ return 1;
+ fi;
+} # Checks if file has valid codec name using ffprobe
+get_audio_format() {
+ # Desc: Gets audio format of file as string
+ # Usage: get_audio_format arg1
+ # Depends: ffprobe
+ # Version: 0.0.1
+ # Input: arg1: input file path
+ # Output: stdout (if valid audio format)
+ # exit code 0 if audio file; 1 otherwise
+ # Example: get_audio_format myvideo.mp4
+ # Note: Would return "opus" if full ffprobe report had 'Audio: opus, 48000 Hz, stereo, fltp'
+ # Note: Not tested with videos containing multiple video streams
+ # Ref/Attrib: [1] https://stackoverflow.com/questions/5618363/is-there-a-way-to-use-ffmpeg-to-determine-the-encoding-of-a-file-before-transcod
+ # [2] https://stackoverflow.com/questions/44123532/how-to-find-out-the-file-extension-for-extracting-audio-tracks-with-ffmpeg-and-p#comment88464070_50723126
+ local audio_format file_in;
+ local return_state;
+ file_in="$1";
+
+ # Return error exit code if not audio file
+ ## Return error if ffprobe itself exited on error
+ 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
+ return_state="false";
+ fi;
+
+ # Get audio format
+ audio_format="$(ffprobe -v error -select_streams a -show_entries stream=codec_name -of default=nokey=1:noprint_wrappers=1 "$file_in")"; # see [1]
+
+ ## Return error if audio format is incorrectly formatted (e.g. reject if contains spaces)
+ pattern="^[[:alnum:]]+$"; # alphanumeric string with no spaces
+ if [[ $audio_format =~ $pattern ]]; then
+ return_state="true";
+ # Report audio format
+ echo "$audio_format";
+ else
+ return_state="false";
+ fi;
+
+ # Report exit code
+ if [[ $return_state = "true" ]]; then
+ return 0;
+ else
+ return 1;
+ fi;
+} # Get audio format as stdout
+checkIsInArray() {
+ # Desc: Checks if input arg is element in array
+ # Usage: checkIsInArray arg1 arg2
+ # Version: 0.0.1
+ # Input: arg1: test string
+ # arg2: array
+ # Output: exit code 0 if test string is in array; 1 otherwise
+ # Example: checkIsInArray "foo" "${myArray[@]}"
+ # Ref/Attrib: [1] How do I check if variable is an array? https://stackoverflow.com/a/27254437
+ # [2] How to pass an array as function argument? https://askubuntu.com/a/674347
+ local return_state input arg1 string_test
+ declare -a arg2 array_test
+ input=("$@") # See [2]
+ arg1="${input[0]}";
+ arg2=("${input[@]:1}");
+ #yell "DEBUG:input:${input[@]}";
+ #yell "DEBUG:arg1:${arg1[@]}";
+ #yell "DEBUG:arg2:${arg2[@]}";
+
+ string_test="$arg1";
+ array_test=("${arg2[@]}");
+
+ #yell "DEBUG:string_test:$string_test";
+ #yell "DEBUG:$(declare -p array_test)";
+ for element in "${array_test[@]}"; do
+ #yell "DEBUG:element:$element";
+ if [[ "$element" =~ ^"$string_test" ]]; then
+ return_state="true";
+ continue;
+ fi;
+ done;
+
+ # Report exit code
+ if [[ $return_state == "true" ]]; then
+ return 0;
+ else
+ return 1;
+ fi;
+} # Check if string is element in array
+check_depends() {
+ if ! command -v ffprobe 1>/dev/random 2>&1; then
+ die "FATAL:Missing ffprobe."; fi;
+};
+main() {
+ # Input: array: music_codecs ("mp3" "aac" ...)
+ # stdin
+ # Output: stdout
+ # Depends:
+ # BK-2020-03: read_stdin() 0.0.1
+
+ # check dependencies
+ check_depends;
+
+ # iterate through stdin lines
+ while read -r line; do
+ # check if file
+ if [[ ! -f $line ]]; then continue; fi; # reject
+
+ # check if valid codec
+ if ! check_parsable_audio_ffprobe "$line"; then
+ continue; fi; # reject
+
+ # Check if desired codec
+ file_format="$(get_audio_format "$line")";
+ if ! checkIsInArray "$file_format" "${music_codecs[@]}"; then
+ continue; fi; # reject
+
+ # Output line to stdout
+ printf "%s\n" "$line";
+ done < <(read_stdin);
+}; # main program
+
+main "$@" 2>/dev/random;
+
+# Author: Steven Baltakatei Sandoval
+# License: GPLv3+