3 # Identification of the script
9 shorthelp
='Usage: '"$(basename "${0}")"' [-b bitrate] [-d dirstoretain] [-l lameopts]
10 [-o outputdirectory] [-r] FILES'
11 longhelp
='This script uses the tools flac, metaflac and lame to convert flac files to
12 mp3s. Accepts both individual files and directories (the latter when using the
15 Explanation of the options:
16 -b bitrate Specifies the bitrate of the converted mp3. Defaults to
17 128. Specify a bitrate of "none" to stop the -b option
18 from being passed to lame.
19 -d dirstoretain Specifies how many directory names to retain when using
20 both -r and -o. Works from right to left. See the -r
21 option for an example.
22 -l lameopts Allows to give custom options to lame. Make sure to
23 surround them with quotes (e.g. -l "-V 6")!
24 -o outputdirectory Specifies the directory to write the mp3s to. If not
25 specified, the mp3s will be written to the directory
26 where the flac files are located.
27 -r Makes the script look for flac files recursively. If
28 this option is specified together with an alternative
29 output directory, the mp3s will be written to the
30 specified output directory plus the current relative
31 path of the flac file. Example:
32 "After Edmund/Hello/01_Thank_God.flac"
34 "<outputdirectory>/After Edmund/Hello/01_Thank_God.mp3."
36 ATTENTION: When using full paths (like
37 /home/user/music/After Edmund/Hello/01_Thank_God.flac),
38 you can determine how many directories the script will
39 retain using the -d option. With -d 2, the full path
40 file just above will be converted to:
41 "<outputdirectory>/After Edmund/Hello/01_Thank_God.mp3."
43 -s Do not use this option unless you know what you are
44 doing! It is used internally to self-execute the
45 script for conversion.
46 -v Be verbose; makes the script tell what it is doing.
48 For more information see http://www.linuxtutorialblog.com/.
49 No warranty included with this script ;-) Rechosen'
51 # Is the user asking for extensive help?
52 if [[ "$*" == *--help* ]]; then
53 echo -e "$shorthelp""\n""$longhelp"
57 # Specify the default variable values
62 recursive
="-maxdepth 0"
68 # Functions for input validation
70 if [[ "$1" == *[^
0-9]* && "$1" != "none" ]]; then
71 echo -e "-$options has to be numeric!\n""$shorthelp"
76 # Get the passed options
78 while getopts ":b:d:l:o:rsv" options
; do
79 optionstopass
[$i]=-"$options"
81 if [ "$OPTARG" ]; then
82 optionstopass
[$i]="$OPTARG"
86 b
) IsNum
"$OPTARG" && bitrate
="$OPTARG";;
87 d
) IsNum
"$OPTARG" && dirstoretain
="$OPTARG";;
88 l
) lameopts
="$OPTARG";;
89 o
) outputdir
="$OPTARG";;
91 s
) selfexecute
="true";;
93 ?
) echo $shorthelp; exit 2;;
97 # Remove parsed options
98 shift $
(($OPTIND - 1))
100 [ "$verbose" ] && echo "Options parsed. Left on the command-line: ""$*"
102 # Do we have any files to convert? If not, show usage.
104 echo -e "No files to convert!\n""$shorthelp"; exit 2
107 # For drag&drop support: we haven't accidentally got a non-supported URL, do we?
108 [ "$verbose" ] && echo "Checking for unsupported URLs..."
110 # Attention: from now on, only ${cmdline[@]} is safe to use to get the files!
111 for (( i
=0; i
<${#cmdline[@]}; i
++ )); do
112 [ "$verbose" ] && echo "Removing any \"file://\" prefixes..."
113 cmdline
[$i]=${cmdline[$i]#"file://"}
114 if [[ "${cmdline[@]}" =~
"^.{1,7}\://" ]]; then
115 echo "Sorry, $(basename "$0") only supports local files."
120 # Make sure $outputdir has an ending "/"
121 if [ -n "$outputdir" -a "${outputdir:(-1)}" != "/" ]; then
122 outputdir
=$outputdir"/"
123 [ "$verbose" ] && echo "Added a trailing \"/\" to \$outputdir."
127 # Are we user-called or self-executing?
128 if [ "$selfexecute" == "false" ]; then
129 # Introduce ourselves
130 echo "Thanks for using $name $version! This script by $author."
132 # Find all flac files and re-execute the script once for every file to
134 [ "$verbose" ] && echo "Using find to get the flac files and re-executing the script for each of them."
135 find "${cmdline[@]}" $recursive -print0 |
grep -zZ -i -E .
+\.flac$ |\
136 xargs -i -0 "$0" "${optionstopass[@]}" -s {}
138 # Start an actual conversion; get the file to convert
139 [ "$verbose" ] && echo "Getting file to convert..."
140 file="$@" # "$@" is safe to use again because of self-execution
141 [ "$verbose" ] && echo "File: ""$file"
142 # Remove any lame-looking "./" in front of the filename
143 if [ "${file:0:2}" == "./" ]; then
145 [ "$verbose" ] && echo "Removed \"./\" from the filename."
149 [ "$verbose" ] && echo "Getting the tags..."
151 for tag
in {"title","artist","album","tracknumber","date","genre","comment"}; do
152 info
[$i]=$
(metaflac
--show-tag=$tag "$file")
153 info
[$i]=${info[$i]#*=}
154 [ "$verbose" ] && echo "Got $tag tag: ""${info[$i]}"
158 # Convert the tags to options for lame
159 [ "$verbose" ] && echo "Converting tags to lame options..."
161 for option
in {"t","a","l","n","y","g","c"}; do
162 if [ "${info[$((i/2))]}" ]; then
163 command[$i]=--t$option
164 command[$
((i
+1))]="${info[$((i/2))]}"
165 if [ "$option" == "c" ]; then
166 command[$
((i
+1))]=${command[$((i+1))]:0:30}
167 elif [ "$option" == "y" ]; then
168 command[$
((i
+1))]=${command[$((i+1))]:0:4}
169 elif [ "$option" == "g" ]; then
170 if ! lame
--genre-list 2>&1 |
grep "${command[$((i+1))]}" >/dev
/null
; then
172 [ "$verbose" ] && echo "Nullified the genre tag as the specified genre does not exist."
178 [ "$verbose" ] && echo "Lame options: ""${command[@]}"
180 # Prepare the outputfile variable
181 [ "$verbose" ] && echo "Preparing \$outputfile..."
182 basefile
=$
(basename "$file")
183 if [ -z "$outputdir" -o -z "$recursive" ]; then
184 # We're going to extend the outputdir using the source dir(s)
185 # Checking if we need to limit the amount of source dirs retained...
186 if [ -n "$dirstoretain" ]; then
188 filedir
=$
(dirname "$file")
190 while [ $i -lt $dirstoretain -a -n "$filedir" ]; do
191 dirtoretain
=`expr "$filedir" : '.*\(/[^/]\+\)'`
192 filedir
=${filedir%$dirtoretain}
193 dirtoretain
=${dirtoretain:1}"/"
194 retaineddirs
="$dirtoretain""$retaineddirs"
197 outputfile
="$outputdir""$retaineddirs""${basefile/.[fF][lL][aA][cC]/.mp3}"
199 # Just throw in all dirs
200 outputfile
="$outputdir""${file/.[fF][lL][aA][cC]/.mp3}"
203 outputfile
="$outputdir""${basefile/.[fF][lL][aA][cC]/.mp3}"
205 [ "$verbose" ] && echo "\$outputfile is ""$outputfile"". Creating directories if needed..."
206 mkdir
-p "$(dirname "$outputfile")"
208 # Prepare the bitrate
209 [ "$verbose" ] && echo "Preparing bitrate..."
210 if [ -n "$bitrate" -a "$bitrate" != "none" ]; then
211 bitratearray
=( "-b" "$bitrate" )
214 # Make the lameopts usable
215 [ "$verbose" ] && echo "Making \$lameopts usable."
217 for l
in $lameopts; do
218 lameoptsarray
[$i]="$l"
222 # Actual conversion coming up...
223 [ "$verbose" ] && echo "Preparing progress indicator..."
224 # Now for the great trick... a progress indicator!
225 flacsize
=$
(stat
-c %s
"$file")
226 blocksize
=$
(( flacsize
/ 100 ))
227 if [ $blocksize -le 0 ]; then
230 ddcount
=$
(( $blocksize / 512 ))
231 if [ $blocksize -le 0 ]; then
237 echo -n "Converting $file to $outputfile..."
239 printf " Time left: %02d:%02d" 0 0
241 while [ $donesofar -lt $flacsize ]; do
242 dd bs
=512 count
=$ddcount 2>/dev
/null
243 (( donesofar
+= $blocksize ))
245 percent
=$
(( 100 * $donesofar / $flacsize ))
246 echo -n -e "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b" 1>&2
247 printf "%4s%%" $percent 1>&2
249 # Calculate the estimated time left
250 timeelapsed
=$
(( $
(date +%s
) - $start ))
251 if [ $timeelapsed -gt 0 ]; then
252 bytespersec
=$
(( $donesofar / $timeelapsed ))
253 totaltimeest
=$
(( $flacsize / $bytespersec + 2 )) # "+ 2" is a correction because flac and lame need to finish up
254 timeleft
=$
(( $totaltimeest - $timeelapsed ))
255 timeleftmin
=$
(( $timeleft / 60 ))
256 timeleftsec
=$
(( $timeleft % 60 ))
257 printf " Time left: %02d:%02d" $timeleftmin $timeleftsec 1>&2
259 printf " Time left: %02d:%02d" 0 0 1>&2
263 lame
-hS "${bitratearray[@]}" "${lameoptsarray[@]}" "${command[@]}" - "$outputfile"
264 echo -e "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b Done. "