[tpwd] Fix segfault when exactly one argument given
[tinyapps.git] / mp3rip
blob73d80e0483dd28c4bb49b0c7cc0822b518d96167
1 #!/bin/sh
2 ##
3 ## Rips audio tracks and encodes them to mp3
4 ## Copyright (c) 2003-2005 by Tomekk (tomekk/AT/oswiecim.eu.org)
5 ## Copyright (c) 2005,2006 by Michal Nazarewicz (mina86/AT/mina86.com)
6 ##
7 ## This program is free software; you can redistribute it and/or modify
8 ## it under the terms of the GNU General Public License as published by
9 ## the Free Software Foundation; either version 3 of the License, or
10 ## (at your option) any later version.
12 ## This program is distributed in the hope that it will be useful,
13 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
14 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 ## GNU General Public License for more details.
17 ## You should have received a copy of the GNU General Public License
18 ## along with this program; if not, see <http://www.gnu.org/licenses/>.
20 ## This is part of Tiny Applications Collection
21 ## -> http://tinyapps.sourceforge.net/
24 set -e
25 echo 'mp3rip v0.5 (c) Tomekk & mina86'
26 echo
28 LOWER_CHARS=abcdefghijklmnopqrstuvwxyz
29 UPPER_CHARS=ABCDEFGHIJKLMNOPQRSTUVWXYZ
33 ## Usage
35 usage () {
36 cat <<EOF
37 usage: ${0##*/} [<options>] [ [--] <tracks> | -a | --ask | --list ]
38 ${0##*/} --setup
40 <options>:
41 -h --help Prints this help screan and exits
42 -V --version Prints version number and exits
43 -H --method-help Prints help about methods and exits
44 -s --setup Runs setup
45 --config Synonym of --setup
47 -v<verbosity> Verbosity level (0 to 5) [2]
48 -v Synonym of -v3
49 -vv Synonym of -v4
51 -d --device=<device> Uses <device> as cdrom [/dev/cdrom]
52 'search' makes cdparanoia search for a drive
53 -m --method=<method> Method to use when encoding [vbr]
54 -b --bitrate=<br> Bitrate used in CBR/ABR method [128]
55 -b --min-bitrate=<br> Mimimum bitrate used in VBR/ABR method [32]
56 -B --max-bitrate=<br> Maximum bitarte used in VBR/ABR method [320]
57 -O --options=<opt> Passes <opt> to lame when in user method
58 -q --quality=<q> Quality to encode with (0-best; 9-worse) [5]
60 -p --pipe Use pipes -- may be a bit faster (implies -v1)
61 -k --keep-wav Doesn't remove wav files after encoding
62 --cd=<dir> Changes directory to <dir> (creates if missing)
64 -l --lame=<lame> Path to lame [auto]
65 -L --lame-opts=<opt> Aditional lame options [none]
66 -c --cdparanoia=<cdp> Path to cdparanoia [auto]
67 -C --cdparanoia-opts=<opt> Aditional cdparanoia options [none]
69 -a --ask Prompts user to enter track numbers to rip
70 -t --list List all audio tracks
71 <tracks> Track numbers to rip [all]
72 EOF
78 ## Description of methods
80 method_help () {
81 cat <<EOF
82 There are fallowing methods available:
83 * cbr (constant bitrate)
84 Encodes MP3 with a constant bitrate specified using -b option.
86 * vbr (variable bitrate)
87 The bitrate of each fragments of MP3 is adjusted automaticaly so
88 that higher bitrates are used only when needed (ie. a few seconds of
89 silence are encoded using minimal bitrate to save some space).
91 * vbr-old (variable bitrate, old routine)
92 Works just like vbr but uses older, slower and most tested VBR algorithm.
94 * abr (average bitrate)
95 Similar to VBR but makes the average bitrate of MP3 file to be
96 exactly the bitrate specified by --bitrate option.
97 Note: Do not use -b option with abr method since it is ambigious.
98 Use --bitrate and --min-bitrate instead.
100 * user
101 You have to specify options used to encode the WAV file by yourself.
103 * rip-only
104 When this method is selected WAV files won't be encoded to MP3s.
111 ## Messages
113 error () {
114 __EC=$1
115 shift
116 echo "${0##*/}: $*"
117 exit $__EC
123 ## Init variables and read config
125 VERBOSITY=2
126 RUN_SETUP=
127 TRACKS=all
128 DEV=/dev/cdrom
129 METHOD=vbr
130 LAME=auto
131 CDPARANOIA=auto
132 LAME_OPTS=
133 CDPARANOIA_OPTS=
134 BR=128
135 MIN_BR=32
136 MAX_BR=320
137 QUALITY=5
138 OPTIONS=
139 PIPE=
140 KEEP_WAV=
143 if [ -e ~/.mp3-opt ]; then
144 . ~/.mp3-opt
146 KEEP_WAV=$(printf "%s" "$KEEP_WAV" | tr $UPPER_CHARS $LOWER_CHARS)
147 if [ "X$KEEP_WAV" != "Xy" ] && [ "X$KEEP_WAV" != "Xyes" ]
148 then KEEP_WAV=; else KEEP_WAV=y; fi
150 PIPE=$(printf "%s" "$PIPE" | tr $UPPER_CHARS $LOWER_CHARS)
151 if [ "X$PIPE" != "Xy" ] && [ "X$PIPE" != "Xyes" ]
152 then PIPE=; else PIPE=y; fi
158 ## Parse arguments
160 while [ $# -ne 0 ] && [ "X$1" != X-- ]; do
161 case "$1" in
162 (-h|--help) usage ; exit 0; ;;
163 (-V|--version) exit 0; ;;
164 (-H|--method-help) method_help ; exit 0; ;;
165 (-s|--setup|--config) RUN_SETUP=y ; ;;
167 (-v[0-5]) VERBOSITY="${1#-v}" ; ;;
168 (-v) VERBOSITY=3 ; ;;
169 (-vv) VERBOSITY=4 ; ;;
171 (-d) DEVICE="$2" ; shift ; ;;
172 (-d*) DEVICE="${1#-d}" ; ;;
173 (--device=*) DEVICE="${1#*=}" ; ;;
174 (-m) METHOD="$2" ; shift ; ;;
175 (-m*) METHOD="${1#-m}" ; ;;
176 (--method=*) METHOD="${1#*=}" ; ;;
178 (-b) BR="$2"; MIN_BR="$2" ; shift ; ;;
179 (-b*) BR="${1#-b}"; MIN_BR="${1#-b}"; ;;
180 (--bitrate=*) BR="${1#*=}" ; ;;
181 (--min-bitrate=*) MIN_BR="${1#*=}" ; ;;
182 (--max-bitrate=*) MAX_BR="${1#*=}" ; ;;
184 (-o) OPTIONS="$2" ; shift ; ;;
185 (-o*) OPTIONS="${1#-o}" ; ;;
186 (--options=*) OPTIONS="${1#*=}" ; ;;
187 (-q) QUALITY="$2" ; shift ; ;;
188 (-q*) QUALITY="${1#-q}" ; ;;
189 (--quality=*) QUALITY="${1#*=}" ; ;;
191 (-p|--pipe) PIPE=y ; ;;
192 (-k|--keep-wav) KEEP_WAV=y ; ;;
193 (--cd=*) CD="${1#*=}" ; ;;
195 (-l) LAME="$2" ; shift ; ;;
196 (-l*) LAME="${1#-l}" ; ;;
197 (--lame=*) LAME="${1#*=}" ; ;;
198 (-L) LAME_OPTS="$2" ; shift ; ;;
199 (-L*) LAME_OPTS="${1#-L}" ; ;;
200 (--lame-opts=*) LAME_OPTS="${1#*=}" ; ;;
202 (-c) CDPARANOIA="$2" ; shift ; ;;
203 (-c*) CDPARANOIA="${1#-c}" ; ;;
204 (--cdparanoia=*) CDPARANOIA="${1#*=}" ; ;;
205 (-c) CDPARANOIA_OPTS="$2" ; shift ; ;;
206 (-c*) CDPARANOIA_OPTS="${1#-C}" ; ;;
207 (--cdparanoia-opts=*) CDPARANOIA_OPTS="${1#*=}" ; ;;
209 (-a|--ask) TRACKS=ask ; ;;
210 (-t|--list) TRACKS=list ; ;;
212 (-*) error 1 "Invalid argument: '$1'"; ;;
213 ([0-9][0-9]) break; ;;
214 ([0-9]) break; ;;
215 esac
216 shift
217 done
219 if [ "X$1" = X-- ]; then
220 shift;
222 if [ "X$TRACKS" = Xall ] && [ $# -ne 0 ]; then
223 TRACKS=args
229 ## Run setup
231 if [ "$RUN_SETUP" ]; then
232 echo '=== Entering setup'
234 ## Function for asking question
235 ask () {
236 __NAME="$1"; shift
237 __VALUE=$(eval echo \"\$$__NAME\")
239 if [ "X$2" = Xbool ]; then
240 if [ -z "$__VALUE" ]; then __VALUE=no; else __VALUE=yes; fi
243 if [ "X$2" != Xnodefault ]; then
244 printf "%s [%s]: " "$1" "$__VALUE"
245 if read __ANS && [ -n "$__ANS" ]; then __VALUE="$__ANS"; fi
246 else
247 printf "%s (%s): " "$1" "$__VALUE"
248 if read __ANS; then __VALUE="$__ANS"; fi
251 printf "\n# %s\n%s='%s'\n" "$1" "$__NAME" \
252 $(printf "%s" "$__VALUE" | sed -e "s/'/'\\\\''/") >>"$TMP_FILE"
254 if [ "X$2" = Xbool ]; then
255 __VALUE=$(printf "%s" "$__VALUE" | tr $UPPER_CHARS $LOWER_CHARS)
256 if [ "X$__VALUE" != "Xy" ] && [ "X$__VALUE" != "Xyes" ]
257 then __VALUE=; else __VALUE=y; fi
260 eval $__NAME=\"\$__VALUE\"
263 ## Temp file
264 if which mktemp >/dev/null 2>&1; then
265 TMP_FILE=$(mktemp)
266 elif which tempfile >/dev/null 2>&1; then
267 TMP_FILE=$(tempfile)
268 else
269 echo unable to create temporary file, \
270 mktemp or tempfile required >&2
271 exit 1
274 ## Config header
275 cat <<EOF >$TMP_FILE
277 ## mp3rip Configuration File
279 ## This file has been generated automaticly.
280 ## To enter setup run mp3rip --setup
284 ## Questions
285 ask VERBOSITY 'Default verbosity level (0 to 5)'
286 ask DEV 'CD-Rom Device'
287 ask METHOD 'Method (see --method-help for more info)'
288 ask BR 'Bitrate (used in cbr and abr methods)'
289 ask MIN_BR 'Mimimum bitrate (used in vbr and abr methods)'
290 ask MAX_BR 'Mimimum bitrate (used in vbr and abr methods)'
291 ask QUALITY 'Encoding quality (0 - best, 9 - worse)'
292 ask OPTIONS 'Options to use in user method'
293 ask LAME 'Lame executable to use'
294 ask LAME_OPTS 'Aditional arguments to lame' nodefault
295 ask CDPARANOIA 'Cdparanoia executable to use'
296 ask CDPARANOIA_OPTS 'Aditional arguments to cdparanoia' nodefault
297 ask PIPE 'Do you want to use pipes' bool
298 ask KEEP_WAV 'Do you want to keep wav files' bool
300 ## Save?
301 DONE=
302 while [ -z "$DONE" ]; do
303 printf 'Do you want to save the options (show to show config file) [yes]: '
304 if ! read __ANS || [ -z "$__ANS" ]; then __ANS=yes; fi
305 __ANS=$(echo "$__ANS" | tr $UPPER_CHARS $LOWER_CHARS)
306 DONE=y
307 if [ "X$__ANS" = Xshow ]; then
308 printf '\n%s\n' '---<cut here>---'
309 cat <"$TMP_FILE"
310 printf '%s\n\n' '---<cut here>---'
311 DONE=
312 elif [ "X$__ANS" = Xy ] || [ "X$__ANS" = Xyes ]; then
313 echo 'Saving options'
314 cat <"$TMP_FILE" >~/.mp3riprc
316 done
317 rm -f "$TMP_FILE"
319 ## Continue
320 printf 'Do you want to rip CD now [no]: '
321 read __ANS
322 __ANS=$(echo "$__ANS" | tr $UPPER_CHARS $LOWER_CHARS)
323 if [ "X$__ANS" != Xy ] && [ "X$__ANS" != Xyes ]; then
324 exit 0;
327 if [ "X$TRACKS" = Xall ]; then
328 TRACKS=ask
335 ## Things concerning verbosity
337 if [ -n "$PIPE" ] && [ $VERBOSITY -gt 1 ]; then VERBOSITY=1; fi
339 if [ $VERBOSITY -gt 2 ]; then V=-v; else V=; fi
341 msg () {
342 if [ $VERBOSITY -ge $1 ]; then
343 shift
344 echo "$*"
351 ## Change directory
353 if [ -n "$CD" ]; then
354 echo "=== Entering directory $CD"
355 mkdir -p $V -- "$CD"
356 cd "$CD"
362 ## Prepare cdparanoia
364 if [ "X$CDPARANOIA" = Xauto ]; then CDPARANOIA=cdparanoia; fi
365 if ! CDPARANOIA=$(which "$CDPARANOIA" 2>/dev/null); then
366 error 2 Could not fine cdparanoia executable
368 msg 4 Cdparanoia executable: $CDPARANOIA
369 CDP="$CDPARANOIA $CDPARANOIA_OPTS"
371 if [ "X$DEV" = Xsearch ]; then
372 CDP="$CDP -s"
373 else
374 CDP="$CDP -d $DEV"
377 if [ $VERBOSITY -eq 0 ]; then CDP="$CDP -q"
378 elif [ $VERBOSITY -ge 4 ]; then CDP="$CDP --verbose"
380 msg 3 Cdparanoia command: $CDP
385 ## List tracks (if --list given)
387 if [ "X$TRACKS" = Xlist ]; then
388 echo === Listing tracks
389 msg 5 Running: $CDP -Q
390 $CDP -Q
391 exit $?
397 ## Get list of tracks to rip
399 if [ "X$TRACKS" = Xask ]; then
400 set --
401 printf 'Enter track numbers to rip (^D or q ends): '
402 while read NUM; do
403 set -- "$@" $NUM
404 if printf " %s " $NUM | grep -iF ' q ' >/dev/null 2>&1; then
405 break
407 done
408 elif [ "X$TRACKS" = Xall ]; then
409 set -- $($CDP -Q 2>&1 | sed -ne \
410 's/^ *\([1-9][0-9]*\)\. *[1-9][0-9]*.*$/\1/ p')
411 msg 2 === $# tracks found
417 ## Prepare lame
419 if [ "X$LAME" = Xauto ]; then LAME=lame; fi
420 if ! LAME=$(which "$LAME" 2>/dev/null); then
421 error 2 Could not fine lame executable
423 msg 4 Lame executable: $LAME
424 LM="$LAME $LAME_OPTS"
426 case "$METHOD" in
427 (cbr) LM="$LM --cbr -q $QUALITY -b $BR" ; ;;
428 (vbr) LM="$LM --vbr-new -V $QUALITY -b $MIN_BR -B $MAX_BR"; ;;
429 (vbr-old) LM="$LM --vbr-old -V $QUALITY -b $MIN_BR -B $MAX_BR"; ;;
430 (abr) LM="$LM --abr $BR -b $MIN_BR -B $MAX_BR"; ;;
431 (user) LM="$LM $OPTIONS" ; ;;
432 (rip-only) LM=; KEEP_WAV=y; PIPE= ; ;;
433 esac
435 if [ $VERBOSITY -eq 0 ]; then LM="$LM --silent"
436 elif [ $VERBOSITY -eq 1 ]; then LM="$LM --nohist"
437 elif [ $VERBOSITY -eq 5 ]; then LM="$LM --verbose"
439 msg 3 Lame command: $LM
444 ## Do the job
446 ALL=$#
447 DONE=1
448 KEEP_WAV="$KEEP_WAV$PIPE"
449 while [ -n "$1" ] && [ "X$1" != Xq ] && [ "X$1" != XQ ]; do
450 ## Check arg
451 MATCH=$(expr "$1" : '[0-9]*')
452 if [ $MATCH -ne ${#1} ]; then
453 echo "${0##*/}: '$1' is not a number, ignoring" >&2
454 shift
455 conitnue;
458 ## Rip
459 NUM=$(printf %02d "$1")
460 printf "=== Ripping track %2d (%2d/%2d)\n" $1 $DONE $ALL
461 if [ -n "$PIPE" ]; then
462 $CDP "$1" - | $LM - "track$NUM.mp3"
463 else
464 $CDP "$1" "track$NUM.wav"
465 if [ -n "$LM" ]; then
466 echo "=== Encoding track %2d (%2d/%2d)" $1 $DONE $ALL
467 $LM "track$NUM.wav" "track$NUM.mp3"
471 ## Remove wav
472 if [ -z "$KEEP_WAV" ]; then
473 rm -f $V "track$NUM.wav"
476 DONE=$((DONE + 1 ))
477 shift
478 done