2 # Desc: Converts file readable by ffmpeg to FLAC audio file
3 # Usage: convert_file_to_flac.sh [path]
5 # Ref/Attrib: [1] Convert audio file to FLAC with ffmpeg? https://superuser.com/a/802126
6 # [2] How to specify flac compression level when converting with avconv? https://askubuntu.com/questions/544651/
7 # [3] How can I extract audio from video with ffmpeg? https://stackoverflow.com/a/27413824
8 # Depends: ffmpeg version 4.4.2, ffprobe version 4.4.2, bash version 5.1.16
10 declare -Ag appRollCall
# Associative array for storing app status
11 declare -Ag fileRollCall
# Associative array for storing file status
12 declare -Ag dirRollCall
# Associative array for storing dir status
14 yell
() { echo "$0: $*" >&2; } # print script path and all args to stderr
15 die
() { yell
"$*"; exit 111; } # same as yell() but non-zero exit status
16 try
() { "$@" || die
"cannot $*"; } # runs args as command, reports args if command fails
18 # Desc: If arg is a command, save result in assoc array 'appRollCall'
19 # Usage: checkapp arg1 arg2 arg3 ...
21 # Input: global assoc. array 'appRollCall'
22 # Output: adds/updates key(value) to global assoc array 'appRollCall'
28 if command -v "$arg" 1>/dev
/null
2>&1; then # Check if arg is a valid command
29 appRollCall
[$arg]="true";
30 if ! [ "$returnState" = "false" ]; then returnState
="true"; fi;
32 appRollCall
[$arg]="false"; returnState
="false";
36 #===Determine function return code===
37 if [ "$returnState" = "true" ]; then
42 } # Check that app exists
44 # Desc: If arg is a file path, save result in assoc array 'fileRollCall'
45 # Usage: checkfile arg1 arg2 arg3 ...
47 # Input: global assoc. array 'fileRollCall'
48 # Output: adds/updates key(value) to global assoc array 'fileRollCall';
49 # Output: returns 0 if app found, 1 otherwise
55 if [ -f "$arg" ]; then
56 fileRollCall
["$arg"]="true";
57 if ! [ "$returnState" = "false" ]; then returnState
="true"; fi;
59 fileRollCall
["$arg"]="false"; returnState
="false";
63 #===Determine function return code===
64 if [ "$returnState" = "true" ]; then
69 } # Check that file exists
71 # Desc: If arg is a dir path, save result in assoc array 'dirRollCall'
72 # Usage: checkdir arg1 arg2 arg3 ...
74 # Input: global assoc. array 'dirRollCall'
75 # Output: adds/updates key(value) to global assoc array 'dirRollCall';
76 # Output: returns 0 if all args are dirs; 1 otherwise
82 if [ -z "$arg" ]; then
83 dirRollCall
["(Unspecified Dirname(s))"]="false"; returnState
="false";
84 elif [ -d "$arg" ]; then
85 dirRollCall
["$arg"]="true";
86 if ! [ "$returnState" = "false" ]; then returnState
="true"; fi
88 dirRollCall
["$arg"]="false"; returnState
="false";
92 #===Determine function return code===
93 if [ "$returnState" = "true" ]; then
98 } # Check that dir exists
100 # Desc: Displays missing apps, files, and dirs
101 # Usage: displayMissing
103 # Input: associative arrays: appRollCall, fileRollCall, dirRollCall
104 # Output: stderr: messages indicating missing apps, file, or dirs
105 # Output: returns exit code 0 if nothing missing; 1 otherwise
106 # Depends: bash 5, checkAppFileDir()
107 local missingApps value appMissing missingFiles fileMissing
108 local missingDirs dirMissing
110 #==BEGIN Display errors==
111 #===BEGIN Display Missing Apps===
112 missingApps
="Missing apps :";
113 #for key in "${!appRollCall[@]}"; do echo "DEBUG:$key => ${appRollCall[$key]}"; done
114 for key
in "${!appRollCall[@]}"; do
115 value
="${appRollCall[$key]}";
116 if [ "$value" = "false" ]; then
117 #echo "DEBUG:Missing apps: $key => $value";
118 missingApps
="$missingApps""$key ";
122 if [ "$appMissing" = "true" ]; then # Only indicate if an app is missing.
123 echo "$missingApps" 1>&2;
126 #===END Display Missing Apps===
128 #===BEGIN Display Missing Files===
129 missingFiles
="Missing files:";
130 #for key in "${!fileRollCall[@]}"; do echo "DEBUG:$key => ${fileRollCall[$key]}"; done
131 for key
in "${!fileRollCall[@]}"; do
132 value
="${fileRollCall[$key]}";
133 if [ "$value" = "false" ]; then
134 #echo "DEBUG:Missing files: $key => $value";
135 missingFiles
="$missingFiles""$key ";
139 if [ "$fileMissing" = "true" ]; then # Only indicate if an app is missing.
140 echo "$missingFiles" 1>&2;
143 #===END Display Missing Files===
145 #===BEGIN Display Missing Directories===
146 missingDirs
="Missing dirs:";
147 #for key in "${!dirRollCall[@]}"; do echo "DEBUG:$key => ${dirRollCall[$key]}"; done
148 for key
in "${!dirRollCall[@]}"; do
149 value
="${dirRollCall[$key]}";
150 if [ "$value" = "false" ]; then
151 #echo "DEBUG:Missing dirs: $key => $value";
152 missingDirs
="$missingDirs""$key ";
156 if [ "$dirMissing" = "true" ]; then # Only indicate if an dir is missing.
157 echo "$missingDirs" 1>&2;
160 #===END Display Missing Directories===
162 #==END Display errors==
163 #==BEGIN Determine function return code===
164 if [ "$appMissing" == "true" ] ||
[ "$fileMissing" == "true" ] ||
[ "$dirMissing" == "true" ]; then
169 #==END Determine function return code===
170 } # Display missing apps, files, dirs
171 check_parsable_audio_ffprobe
() {
172 # Desc: Checks if ffprobe returns valid audio codec name for file
173 # Usage: check_parsable_audio_ffprobe [path FILE]
175 # Input: arg1: file path
176 # Output: exit code 0 if returns valid codec name; 1 otherwise
177 # Depends: ffprobe, die()
178 local file_in ffprobe_out
180 if [[ $# -ne 1 ]]; then die
"ERROR:Invalid number of args:$#"; fi;
184 # Check if ffprobe detects an audio stream
185 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
188 return_state
="false";
191 # Fail if ffprobe returns no result
192 ffprobe_out
="$(ffprobe -v error -select_streams a -show_entries stream=codec_name -of default=nokey=1:noprint_wrappers=1 "$file_in")";
193 if [[ -z $ffprobe_out ]]; then
194 return_state
="false";
198 if [[ $return_state = "true" ]]; then
203 } # Checks if file has valid codec name using ffprobe
206 if [[ ! -f $1 ]]; then die
"FATAL:Not a file:$1"; fi;
207 if [[ $# -ne 1 ]]; then die
"FATAL:Arguments not equal to 1:$#"; fi;
210 if ! checkapp ffmpeg ffprobe
; then
212 die
"FATAL:Missing apps"; fi;
215 # Check file is parsable by ffprobe
216 if ! check_parsable_audio_ffprobe
"$1"; then
217 die
"FATAL:Not an audio file:$1"; fi;
219 # Convert file to FLAC. See [1], [2]
220 try ffmpeg
-i "$1" -vn -c:a flac
-map_metadata 0 -compression_level 12 "$1".flac
225 # Author: Steven Baltakatei Sandoval