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 
-compression_level 12 "$1".flac
 
 225 # Author: Steven Baltakatei Sandoval