Commit | Line | Data |
---|---|---|
54469ce4 SBS |
1 | #!/bin/bash |
2 | # Desc: Converts file readable by ffmpeg to FLAC audio format | |
3 | # Usage: convert_file_to_flac.sh [path] | |
4 | # Version: 0.0.1 | |
5 | # Ref/Attrib: [1] Convert audio file to FLAC with ffmpeg? https://superuser.com/a/802126 | |
6 | ||
7 | declare -Ag appRollCall # Associative array for storing app status | |
8 | declare -Ag fileRollCall # Associative array for storing file status | |
9 | declare -Ag dirRollCall # Associative array for storing dir status | |
10 | ||
11 | yell() { echo "$0: $*" >&2; } # print script path and all args to stderr | |
12 | die() { yell "$*"; exit 111; } # same as yell() but non-zero exit status | |
13 | try() { "$@" || die "cannot $*"; } # runs args as command, reports args if command fails | |
14 | checkapp() { | |
15 | # Desc: If arg is a command, save result in assoc array 'appRollCall' | |
16 | # Usage: checkapp arg1 arg2 arg3 ... | |
17 | # Version: 0.1.1 | |
18 | # Input: global assoc. array 'appRollCall' | |
19 | # Output: adds/updates key(value) to global assoc array 'appRollCall' | |
20 | # Depends: bash 5.0.3 | |
21 | local returnState | |
22 | ||
23 | #===Process Args=== | |
24 | for arg in "$@"; do | |
25 | if command -v "$arg" 1>/dev/null 2>&1; then # Check if arg is a valid command | |
26 | appRollCall[$arg]="true"; | |
27 | if ! [ "$returnState" = "false" ]; then returnState="true"; fi; | |
28 | else | |
29 | appRollCall[$arg]="false"; returnState="false"; | |
30 | fi; | |
31 | done; | |
32 | ||
33 | #===Determine function return code=== | |
34 | if [ "$returnState" = "true" ]; then | |
35 | return 0; | |
36 | else | |
37 | return 1; | |
38 | fi; | |
39 | } # Check that app exists | |
40 | checkfile() { | |
41 | # Desc: If arg is a file path, save result in assoc array 'fileRollCall' | |
42 | # Usage: checkfile arg1 arg2 arg3 ... | |
43 | # Version: 0.1.1 | |
44 | # Input: global assoc. array 'fileRollCall' | |
45 | # Output: adds/updates key(value) to global assoc array 'fileRollCall'; | |
46 | # Output: returns 0 if app found, 1 otherwise | |
47 | # Depends: bash 5.0.3 | |
48 | local returnState | |
49 | ||
50 | #===Process Args=== | |
51 | for arg in "$@"; do | |
52 | if [ -f "$arg" ]; then | |
53 | fileRollCall["$arg"]="true"; | |
54 | if ! [ "$returnState" = "false" ]; then returnState="true"; fi; | |
55 | else | |
56 | fileRollCall["$arg"]="false"; returnState="false"; | |
57 | fi; | |
58 | done; | |
59 | ||
60 | #===Determine function return code=== | |
61 | if [ "$returnState" = "true" ]; then | |
62 | return 0; | |
63 | else | |
64 | return 1; | |
65 | fi; | |
66 | } # Check that file exists | |
67 | checkdir() { | |
68 | # Desc: If arg is a dir path, save result in assoc array 'dirRollCall' | |
69 | # Usage: checkdir arg1 arg2 arg3 ... | |
70 | # Version 0.1.2 | |
71 | # Input: global assoc. array 'dirRollCall' | |
72 | # Output: adds/updates key(value) to global assoc array 'dirRollCall'; | |
73 | # Output: returns 0 if all args are dirs; 1 otherwise | |
74 | # Depends: Bash 5.0.3 | |
75 | local returnState | |
76 | ||
77 | #===Process Args=== | |
78 | for arg in "$@"; do | |
79 | if [ -z "$arg" ]; then | |
80 | dirRollCall["(Unspecified Dirname(s))"]="false"; returnState="false"; | |
81 | elif [ -d "$arg" ]; then | |
82 | dirRollCall["$arg"]="true"; | |
83 | if ! [ "$returnState" = "false" ]; then returnState="true"; fi | |
84 | else | |
85 | dirRollCall["$arg"]="false"; returnState="false"; | |
86 | fi | |
87 | done | |
88 | ||
89 | #===Determine function return code=== | |
90 | if [ "$returnState" = "true" ]; then | |
91 | return 0; | |
92 | else | |
93 | return 1; | |
94 | fi | |
95 | } # Check that dir exists | |
96 | displayMissing() { | |
97 | # Desc: Displays missing apps, files, and dirs | |
98 | # Usage: displayMissing | |
99 | # Version 1.0.0 | |
100 | # Input: associative arrays: appRollCall, fileRollCall, dirRollCall | |
101 | # Output: stderr: messages indicating missing apps, file, or dirs | |
102 | # Output: returns exit code 0 if nothing missing; 1 otherwise | |
103 | # Depends: bash 5, checkAppFileDir() | |
104 | local missingApps value appMissing missingFiles fileMissing | |
105 | local missingDirs dirMissing | |
106 | ||
107 | #==BEGIN Display errors== | |
108 | #===BEGIN Display Missing Apps=== | |
109 | missingApps="Missing apps :"; | |
110 | #for key in "${!appRollCall[@]}"; do echo "DEBUG:$key => ${appRollCall[$key]}"; done | |
111 | for key in "${!appRollCall[@]}"; do | |
112 | value="${appRollCall[$key]}"; | |
113 | if [ "$value" = "false" ]; then | |
114 | #echo "DEBUG:Missing apps: $key => $value"; | |
115 | missingApps="$missingApps""$key "; | |
116 | appMissing="true"; | |
117 | fi; | |
118 | done; | |
119 | if [ "$appMissing" = "true" ]; then # Only indicate if an app is missing. | |
120 | echo "$missingApps" 1>&2; | |
121 | fi; | |
122 | unset value; | |
123 | #===END Display Missing Apps=== | |
124 | ||
125 | #===BEGIN Display Missing Files=== | |
126 | missingFiles="Missing files:"; | |
127 | #for key in "${!fileRollCall[@]}"; do echo "DEBUG:$key => ${fileRollCall[$key]}"; done | |
128 | for key in "${!fileRollCall[@]}"; do | |
129 | value="${fileRollCall[$key]}"; | |
130 | if [ "$value" = "false" ]; then | |
131 | #echo "DEBUG:Missing files: $key => $value"; | |
132 | missingFiles="$missingFiles""$key "; | |
133 | fileMissing="true"; | |
134 | fi; | |
135 | done; | |
136 | if [ "$fileMissing" = "true" ]; then # Only indicate if an app is missing. | |
137 | echo "$missingFiles" 1>&2; | |
138 | fi; | |
139 | unset value; | |
140 | #===END Display Missing Files=== | |
141 | ||
142 | #===BEGIN Display Missing Directories=== | |
143 | missingDirs="Missing dirs:"; | |
144 | #for key in "${!dirRollCall[@]}"; do echo "DEBUG:$key => ${dirRollCall[$key]}"; done | |
145 | for key in "${!dirRollCall[@]}"; do | |
146 | value="${dirRollCall[$key]}"; | |
147 | if [ "$value" = "false" ]; then | |
148 | #echo "DEBUG:Missing dirs: $key => $value"; | |
149 | missingDirs="$missingDirs""$key "; | |
150 | dirMissing="true"; | |
151 | fi; | |
152 | done; | |
153 | if [ "$dirMissing" = "true" ]; then # Only indicate if an dir is missing. | |
154 | echo "$missingDirs" 1>&2; | |
155 | fi; | |
156 | unset value; | |
157 | #===END Display Missing Directories=== | |
158 | ||
159 | #==END Display errors== | |
160 | #==BEGIN Determine function return code=== | |
161 | if [ "$appMissing" == "true" ] || [ "$fileMissing" == "true" ] || [ "$dirMissing" == "true" ]; then | |
162 | return 1; | |
163 | else | |
164 | return 0; | |
165 | fi | |
166 | #==END Determine function return code=== | |
167 | } # Display missing apps, files, dirs | |
168 | check_parsable_audio_ffprobe() { | |
169 | # Desc: Checks if ffprobe returns valid audio codec name for file | |
170 | # Usage: check_parsable_audio_ffprobe [path FILE] | |
171 | # Version: 0.0.1 | |
172 | # Input: arg1: file path | |
173 | # Output: exit code 0 if returns valid codec name; 1 otherwise | |
174 | # Depends: ffprobe, die() | |
175 | local file_in ffprobe_out | |
176 | ||
177 | if [[ $# -ne 1 ]]; then die "ERROR:Invalid number of args:$#"; fi; | |
178 | ||
179 | file_in="$1"; | |
180 | ||
181 | # Check if ffprobe detects an audio stream | |
182 | 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 | |
183 | return_state="true"; | |
184 | else | |
185 | return_state="false"; | |
186 | fi; | |
187 | ||
188 | # Fail if ffprobe returns no result | |
189 | ffprobe_out="$(ffprobe -v error -select_streams a -show_entries stream=codec_name -of default=nokey=1:noprint_wrappers=1 "$file_in")"; | |
190 | if [[ -z $ffprobe_out ]]; then | |
191 | return_state="false"; | |
192 | fi; | |
193 | ||
194 | # Report exit code | |
195 | if [[ $return_state = "true" ]]; then | |
196 | return 0; | |
197 | else | |
198 | return 1; | |
199 | fi; | |
200 | } # Checks if file has valid codec name using ffprobe | |
201 | main() { | |
202 | # Check inputs | |
203 | if [[ ! -f $1 ]]; then die "FATAL:Not a file:$1"; fi; | |
204 | if [[ $# -ne 1 ]]; then die "FATAL:Arguments not equal to 1:$#"; fi; | |
205 | ||
206 | # Check depends | |
207 | if ! checkapp ffmpeg ffprobe; then | |
208 | displayMissing; | |
209 | die "FATAL:Missing apps"; fi; | |
210 | ||
211 | ||
212 | # Check file is parsable by ffprobe | |
213 | if ! check_parsable_audio_ffprobe "$1"; then | |
214 | die "FATAL:Not an audio file:$1"; fi; | |
215 | ||
216 | # Convert file to FLAC | |
217 | try ffmpeg -i "$1" -f "$1".flac | |
218 | }; # main program | |
219 | ||
220 | main "$@"; | |
221 | ||
222 | # Author: Steven Baltakatei Sandoval | |
223 | # License: GPLv3+ |