508 lines
13 KiB
Bash
508 lines
13 KiB
Bash
#!/bin/bash
|
|
|
|
setupDestination() {
|
|
cat <<-EODesc
|
|
|
|
Format:
|
|
copy, vorbis, opus or mp3. Other formats may appear in the future.
|
|
EODesc
|
|
comeagain() {
|
|
read \
|
|
-e \
|
|
${destinationformat["$destination"]+-i"${destinationformat["$destination"]}"}\
|
|
-p 'Format: ' \
|
|
value
|
|
case "$value" in
|
|
'mp3')
|
|
destinationformat["$destination"]=mp3
|
|
lameneeded=1
|
|
;;
|
|
'opus')
|
|
destinationformat["$destination"]=opus
|
|
opusencneeded=1
|
|
;;
|
|
'vorbis'|'ogg')
|
|
destinationformat["$destination"]=vorbis
|
|
oggencneeded=1
|
|
;;
|
|
'copy')
|
|
destinationformat["$destination"]=copy
|
|
;;
|
|
*)
|
|
echo "Unsupported destination format: $value" >&2
|
|
comeagain
|
|
;;
|
|
esac
|
|
}
|
|
comeagain
|
|
cat <<-EODesc
|
|
|
|
Path (path):
|
|
Where to store transcoded files (will be created if it does not
|
|
exist).
|
|
EODesc
|
|
read \
|
|
-e \
|
|
-p'Path: ' \
|
|
${destinationpath["$destination"]+-i"${destinationpath["$destination"]}"}\
|
|
destinationpath["$destination"]
|
|
case ${destinationformat["$destination"]} in
|
|
copy)
|
|
:
|
|
;;
|
|
vorbis)
|
|
cat <<-EODesc
|
|
|
|
Quality (integer):
|
|
The quality parameter of oggenc. See man oggenc for more info.
|
|
EODesc
|
|
expr='^[0-9]*$'
|
|
comeagain() {
|
|
read \
|
|
-p'Quality: ' \
|
|
-e \
|
|
-i \
|
|
${destinationquality["$destination"]:-3}\
|
|
value
|
|
if ! [[ $value =~ $expr ]]
|
|
then
|
|
echo "Invalid quality value: $value" >&2
|
|
comeagain
|
|
fi
|
|
}
|
|
comeagain
|
|
if [ -n "${destinationquality["$destination"]}" ] \
|
|
&& (( value != ${destinationquality["$destination"]} ))
|
|
then
|
|
setupRegen quality
|
|
fi
|
|
destinationquality["$destination"]=$value
|
|
;;
|
|
opus)
|
|
cat <<-EODesc
|
|
|
|
Bitrate (kbps, integer):
|
|
Set (VBR) bitrate to <bitrate>. Note that while Opus allows for
|
|
decimal values, AtOM does not. The reason for this is simple: we do
|
|
numeric comparisons, and Bash only manipulates integers.
|
|
EODesc
|
|
expr='^[0-9]*$'
|
|
comeagain() {
|
|
read \
|
|
-e \
|
|
-i \
|
|
${destinationquality["$destination"]:-128}\
|
|
-p 'Bitrate: ' \
|
|
value
|
|
if ! [[ $value =~ $expr ]]
|
|
then
|
|
echo "Invalid bitrate value: $value" >&2
|
|
comeagain
|
|
fi
|
|
}
|
|
comeagain
|
|
if [ -n "${destinationquality["$destination"]}" ] \
|
|
&& (( value != ${destinationquality["$destination"]} ))
|
|
then
|
|
setupRegen bitrate
|
|
fi
|
|
destinationquality["$destination"]=$value
|
|
cat <<-EODesc
|
|
|
|
Loss (percent, integer):
|
|
If you intend to stream the resulting files over an unreliable
|
|
protocol, you may want to make use of Opus' Forward Error
|
|
Correction algorythm. See the Opus-codec.org website for details.
|
|
EODesc
|
|
comeagain() {
|
|
read \
|
|
-e \
|
|
-i \
|
|
${destinationloss["$destination"]:-0}\
|
|
-p 'Loss: ' \
|
|
value
|
|
if ! [[ $value =~ $expr ]]
|
|
then
|
|
echo "Invalid loss value: $value" >&2
|
|
comeagain
|
|
fi
|
|
}
|
|
comeagain
|
|
if [ -n "${destinationloss["$destination"]}" ] \
|
|
&& (( value != ${destinationloss["$destination"]} ))
|
|
then
|
|
setupRegen loss
|
|
fi
|
|
destinationloss["$destination"]=$value
|
|
;;
|
|
mp3)
|
|
cat <<-EODesc
|
|
|
|
Bitrate (kbps, integer):
|
|
Set (ABR) bitrate to <bitrate>.
|
|
EODesc
|
|
expr='^[0-9]*$'
|
|
comeagain() {
|
|
read \
|
|
-e \
|
|
-i \
|
|
${destinationquality["$destination"]:-128}\
|
|
-p 'Bitrate: ' \
|
|
value
|
|
if ! [[ $value =~ $expr ]]
|
|
then
|
|
echo "Invalid bitrate value: $value" >&2
|
|
comeagain
|
|
fi
|
|
}
|
|
comeagain
|
|
if [ -n "${destinationquality["$destination"]}" ] \
|
|
&& (( value != ${destinationquality["$destination"]} ))
|
|
then
|
|
setupRegen bitrate
|
|
fi
|
|
destinationquality["$destination"]=$value
|
|
cat <<-EODesc
|
|
|
|
Prevent resampling (boolean):
|
|
LAME may decide to encode your file to a lower sampling-rate if you
|
|
use a low bitrate. Setting this to yes will append --resample
|
|
<original file's rate>, preventing any resampling from happening.
|
|
EODesc
|
|
case ${destinationnoresample["$destination"]} in
|
|
0) initialvalue=n ;;
|
|
1) initialvalue=y ;;
|
|
*) unset initialvalue ;;
|
|
esac
|
|
comeagain() {
|
|
read \
|
|
-e \
|
|
${initialvalue+-i $initialvalue}\
|
|
-p'Prevent resampling (y/N): ' \
|
|
value
|
|
case $value in
|
|
[yY])
|
|
[[ $initialvalue == n ]]\
|
|
&& setupRegen noresample
|
|
destinationnoresample["$destination"]=1
|
|
;;
|
|
''|[nN])
|
|
[[ $initialvalue == y ]]\
|
|
&& setupRegen noresample
|
|
destinationnoresample["$destination"]=0
|
|
;;
|
|
*)
|
|
comeagain
|
|
;;
|
|
esac
|
|
}
|
|
comeagain
|
|
;;
|
|
esac
|
|
cat <<-EODesc
|
|
|
|
[Optional parameters]
|
|
Now you will have the opportunity to configure "advanced" parameters
|
|
for $destination. You may leave any of these fields blank.
|
|
EODesc
|
|
if [[ ${destinationformat["$destination"]} != copy ]]
|
|
then
|
|
cat <<-EODesc
|
|
|
|
Normalize (boolean):
|
|
Normalize output files.
|
|
EODesc
|
|
case ${destinationnormalize["$destination"]} in
|
|
0) initialvalue=n ;;
|
|
1) initialvalue=y ;;
|
|
*) unset initialvalue ;;
|
|
esac
|
|
comeagain() {
|
|
read \
|
|
-e \
|
|
${initialvalue+-i $initialvalue}\
|
|
-p'Normalize (y/N): ' \
|
|
value
|
|
case $value in
|
|
[yY])
|
|
[[ $initialvalue == n ]] \
|
|
&& setupRegen normalize
|
|
destinationnormalize["$destination"]=1
|
|
;;
|
|
''|[nN])
|
|
[[ $initialvalue == y ]] \
|
|
&& setupRegen normalize
|
|
destinationnormalize["$destination"]=0
|
|
;;
|
|
*)
|
|
comeagain
|
|
;;
|
|
esac
|
|
}
|
|
comeagain
|
|
fi
|
|
cat <<-EODesc
|
|
|
|
Rename (string):
|
|
Destination files will be named according to <string>, after
|
|
expansion of special strings:
|
|
%{album},
|
|
%{albumartist},
|
|
%{artist},
|
|
%{disc},
|
|
%{genre},
|
|
%{title},
|
|
%{track},
|
|
%{year}.
|
|
Untagged files or files in unrecognized formats will not be changed.
|
|
Surrounding a field with [] makes it optional, meaning renaming
|
|
will still happen if the corresponding tag is not defined.
|
|
|
|
Leave blank if you don't want file renaming.
|
|
EODesc
|
|
initialvalue="${destinationrenamepath["$destination"]}"
|
|
initialvalue+=/
|
|
[[ $initialvalue == / ]] && unset initialvalue
|
|
initialvalue+="${destinationrename["$destination"]}"
|
|
[ -z "$initialvalue" ] && unset initialvalue
|
|
read \
|
|
-e \
|
|
${initialvalue+-i"$initialvalue"} \
|
|
-p'Rename pattern: ' \
|
|
value
|
|
if [[ $value =~ / ]]
|
|
then
|
|
destinationrenamepath["$destination"]="${value%/*}"
|
|
fi
|
|
destinationrename["$destination"]="${value##*/}"
|
|
cat <<-EODesc
|
|
|
|
FAT32 Compatibility (boolean):
|
|
Rename files for compatibility with FAT32 filesystems.
|
|
EODesc
|
|
case ${destinationfat32compat["$destination"]} in
|
|
0) initialvalue=n ;;
|
|
1) initialvalue=y ;;
|
|
*) unset initialvalue ;;
|
|
esac
|
|
comeagain() {
|
|
read \
|
|
-e \
|
|
${initialvalue+-i $initialvalue}\
|
|
-p'FAT32 Compatibility (y/N): ' \
|
|
value
|
|
case $value in
|
|
[yY])
|
|
destinationfat32compat["$destination"]=1
|
|
;;
|
|
''|[nN])
|
|
destinationfat32compat["$destination"]=0
|
|
;;
|
|
*)
|
|
comeagain
|
|
;;
|
|
esac
|
|
}
|
|
comeagain
|
|
cat <<-EODesc
|
|
|
|
ASCII-only filenames (boolean):
|
|
Rename files for compatibility with ASCII-only systems (most car
|
|
radios).
|
|
Uses Perl with Text::Unidecode to replace cyrillic or kanji
|
|
with an ASCII representation.
|
|
EODesc
|
|
case ${destinationascii["$destination"]} in
|
|
0) initialvalue=n ;;
|
|
1) initialvalue=y ;;
|
|
*) unset initialvalue ;;
|
|
esac
|
|
comeagain() {
|
|
read \
|
|
-e \
|
|
${initialvalue+-i $initialvalue}\
|
|
-p'ASCII-only filenames (y/N): '\
|
|
value
|
|
case $value in
|
|
[yY])
|
|
destinationascii["$destination"]=1
|
|
;;
|
|
''|[nN])
|
|
destinationascii["$destination"]=0
|
|
;;
|
|
*)
|
|
comeagain
|
|
;;
|
|
esac
|
|
}
|
|
comeagain
|
|
cat <<-EODesc
|
|
|
|
Skip mime-type (mime-type, string):
|
|
Files with mime-type <mime-type> will not be included in that
|
|
destination. The '*' character is a wildcard.
|
|
|
|
This prompt will loop until an empty string is encountered.
|
|
EODesc
|
|
while [[ ${destinationskipmime["$destination"]} =~ \| ]]
|
|
do
|
|
skippedmimes+=("${destinationskipmime["$destination"]%%|*}")
|
|
destinationskipmime["$destination"]="${destinationskipmime["$destination"]#*|}"
|
|
done
|
|
[ -n "${destinationskipmime["$destination"]}" ] \
|
|
&& skippedmimes+=("${destinationskipmime["$destination"]}")
|
|
count=${#skippedmimes[@]}
|
|
unset destinationskipmime["$destination"]
|
|
for (( i=0 ; 1 ; i++ ))
|
|
do
|
|
read \
|
|
-e \
|
|
${skippedmimes[i]+-i"${skippedmimes[i]}"} \
|
|
-p 'Skip mime-type: ' \
|
|
value
|
|
if [ -n "$value" ]
|
|
then
|
|
destinationskipmime[$destination]="${destinationskipmime[$destination]:+${destinationskipmime[$destination]}|}$value"
|
|
elif (( i < count ))
|
|
then
|
|
continue
|
|
else
|
|
break
|
|
fi
|
|
done
|
|
unset skippedmimes
|
|
if [[ ${destinationformat["$destination"]} != copy ]]
|
|
then
|
|
cat <<-EODesc
|
|
|
|
Copy mime-type (mime-type, string):
|
|
Files with mime-type <mime-type> will be copied as-is to the
|
|
destination. E.g. image/* will copy covers and other images to the
|
|
destination. The '*' character is a wildcard.
|
|
|
|
This prompt will loop until an empty string is encountered.
|
|
EODesc
|
|
while [[ ${destinationcopymime["$destination"]} =~ \| ]]
|
|
do
|
|
copiedmimes+=("${destinationcopymime["$destination"]%%|*}")
|
|
destinationcopymime["$destination"]="${destinationcopymime["$destination"]#*|}"
|
|
done
|
|
[ -n "${destinationcopymime["$destination"]}" ] \
|
|
&& copiedmimes+=("${destinationcopymime["$destination"]}")
|
|
count=${#copiedmimes[@]}
|
|
unset destinationcopymime["$destination"]
|
|
for (( i=0 ; 1 ; i++ ))
|
|
do
|
|
read \
|
|
-e \
|
|
${copiedmimes[i]+-i"${copiedmimes[i]}"} \
|
|
-p 'Copy mime-type: ' \
|
|
value
|
|
if [ -n "$value" ]
|
|
then
|
|
destinationcopymime[$destination]="${destinationcopymime[$destination]:+${destinationcopymime[$destination]}|}$value"
|
|
elif (( i < count ))
|
|
then
|
|
continue
|
|
else
|
|
break
|
|
fi
|
|
done
|
|
unset copiedmimes
|
|
cat <<-EODesc
|
|
|
|
Channels (integer):
|
|
Produced files should have this many channels, no more, no less.
|
|
EODesc
|
|
expr='^[0-9]*$'
|
|
comeagain() {
|
|
read \
|
|
-e \
|
|
${destinationchannels["$destination"]+-i${destinationchannels["$destination"]}}\
|
|
-p'Channel count: ' \
|
|
value
|
|
if ! [[ $value =~ $expr ]]
|
|
then
|
|
echo "Invalid channel count: $value" >&2
|
|
comeagain
|
|
fi
|
|
}
|
|
comeagain
|
|
if [ -n "${destinationchannels["$destination"]}" ] \
|
|
&& (( value != ${destinationchannels["$destination"]} ))
|
|
then
|
|
setupRegen channels
|
|
fi
|
|
destinationchannels["$destination"]=$value
|
|
cat <<-EODesc
|
|
|
|
Sampling rate (Hertz, integer):
|
|
Files will be resampled as needed to the specified sampling-rate.
|
|
Shoutcast/Icecast streams require a constant sampling-rate.
|
|
Telephony systems often require a sampling-rate of 8000Hz.
|
|
EODesc
|
|
if [[ ${destinationformat["$destination"]} == opus ]]
|
|
then
|
|
cat <<-EODesc
|
|
|
|
Please note that Opus supports only the following sample-rates:
|
|
8000, 12000, 16000, 24000, and 48000 Hz. So don't set
|
|
resampling on an Opus destination to any other value or files
|
|
will be resampled twice.
|
|
EODesc
|
|
fi
|
|
comeagain() {
|
|
read \
|
|
-e \
|
|
${destinationfrequency["$destination"]+-i${destinationfrequency["$destination"]}}\
|
|
-p'Sampling-rate: ' \
|
|
value
|
|
if ! [[ $value =~ $expr ]]
|
|
then
|
|
echo "Invalid frequency value: $value" >&2
|
|
comeagain
|
|
fi
|
|
}
|
|
comeagain
|
|
if [ -n "${destinationfrequency["$destination"]}" ] \
|
|
&& (( value != ${destinationfrequency["$destination"]} ))
|
|
then
|
|
setupRegen frequency
|
|
fi
|
|
destinationfrequency["$destination"]=$value
|
|
cat <<-EODesc
|
|
|
|
Higher-Than (bitrate, integer):
|
|
Only reencode files with bitrates higher then <bitrate>kbps. This
|
|
only applies if sample-rate, channel count and of course format are
|
|
equal. If unset, only files with bitrates equal to that of the
|
|
target will be copied (actually, hardlinking will be attempted
|
|
first).
|
|
|
|
As Ogg Vorbis target quality is not defined by its bitrate, Ogg
|
|
Vorbis files will always be reencoded if unset.
|
|
EODesc
|
|
comeagain() {
|
|
read \
|
|
-e \
|
|
${destinationmaxbps["$destination"]+-i${destinationmaxbps["$destination"]}}\
|
|
-p'Higher-Than: ' \
|
|
value
|
|
if ! [[ $value =~ $expr ]]
|
|
then
|
|
echo "Invalid higher-than bitrate value: $value" >&2
|
|
comeagain
|
|
fi
|
|
}
|
|
comeagain
|
|
if [ -n "${destinationmaxbps["$destination"]}" ] \
|
|
&& (( value != ${destinationmaxbps["$destination"]} ))
|
|
then
|
|
setupRegen maxbps
|
|
fi
|
|
destinationmaxbps[$destination]="$value"
|
|
unset regen
|
|
unset expr
|
|
fi
|
|
}
|