2013-03-03 00:01:20 +01:00
|
|
|
|
#!/bin/sh
|
|
|
|
|
|
2013-08-03 21:27:11 +02:00
|
|
|
|
################################################################################
|
|
|
|
|
## User options
|
|
|
|
|
|
|
|
|
|
## You can easily set output folder to current folder with OUTPUT_ROOT=".".
|
2013-08-03 22:17:26 +02:00
|
|
|
|
[ -z "$OUTPUT_ROOT" ] && OUTPUT_ROOT="$HOME/musics"
|
2013-08-03 21:27:11 +02:00
|
|
|
|
|
|
|
|
|
## End of user options
|
|
|
|
|
################################################################################
|
|
|
|
|
|
2013-03-03 00:01:20 +01:00
|
|
|
|
## For the sake of simplicity we convert everything to OGG.
|
|
|
|
|
## OGG quality ranges from -1 to 10.
|
|
|
|
|
## -q-1 45 kbit/s
|
|
|
|
|
## -q0 64 kbit/s
|
|
|
|
|
## -q1 80 kbit/s
|
|
|
|
|
## -q2 96 kbit/s
|
|
|
|
|
## -q3 112 kbit/s
|
|
|
|
|
## -q4 128 kbit/s
|
|
|
|
|
## -q5 160 kbit/s
|
|
|
|
|
## -q6 192 kbit/s
|
|
|
|
|
## -q7 224 kbit/s
|
|
|
|
|
## -q8 256 kbit/s
|
|
|
|
|
## -q9 320 kbit/s
|
|
|
|
|
## -q10 500 kbit/s
|
|
|
|
|
|
2013-05-02 12:34:17 +02:00
|
|
|
|
## Artists: we use same value for artist and album_artist.
|
|
|
|
|
## Genres: since this is not universal by nature, we do not put a genre in tags,
|
|
|
|
|
## except for special cases like Soundtrack.
|
2013-03-05 21:53:29 +01:00
|
|
|
|
## Composer: not universal neither, we prefer ARTIST over COMPOSER, so COMPOSER
|
|
|
|
|
## will be empty.
|
2013-03-03 00:01:20 +01:00
|
|
|
|
|
2013-08-03 21:27:11 +02:00
|
|
|
|
## This script is supposed to work on a per-album basis. Folders will not be
|
2013-03-03 00:01:20 +01:00
|
|
|
|
## accepted as argument. This function will work best if all tracks of a folder
|
2013-03-05 21:53:29 +01:00
|
|
|
|
## belong to the same album. There's is no way to handle covers reliably, so you
|
2013-08-03 21:27:11 +02:00
|
|
|
|
## should remove the undesired covers from the working folder.
|
2013-03-03 00:01:20 +01:00
|
|
|
|
|
2013-03-05 21:53:29 +01:00
|
|
|
|
## TODO: cover resolution check. Should be > 100x100. Above 1000x1000 should
|
|
|
|
|
## give a warning.
|
2013-03-03 00:01:20 +01:00
|
|
|
|
## TODO: extract cover from tags if checksum does not match any from current folder.
|
|
|
|
|
|
|
|
|
|
_printhelp ()
|
|
|
|
|
{
|
2013-08-03 21:27:11 +02:00
|
|
|
|
cat <<EOF | less
|
2013-03-03 11:19:50 +01:00
|
|
|
|
Usage: ${1##*/} [OPTIONS] FILE
|
2013-03-03 00:01:20 +01:00
|
|
|
|
|
|
|
|
|
Options:
|
2013-05-02 12:34:17 +02:00
|
|
|
|
-c : capital case (only first letter in upper case)
|
|
|
|
|
-f : overwrite if file exists
|
2013-03-03 00:01:20 +01:00
|
|
|
|
-p : preview (do not change file)
|
2013-10-08 22:17:30 +02:00
|
|
|
|
-q : hide FFmpeg runtime output.
|
2013-03-03 00:01:20 +01:00
|
|
|
|
-s : skip encoding
|
|
|
|
|
|
|
|
|
|
Tags:
|
|
|
|
|
-a : artist
|
|
|
|
|
-b : bitrate
|
|
|
|
|
-d : date
|
|
|
|
|
-g : genre
|
|
|
|
|
-l : album
|
|
|
|
|
-n : track number
|
2013-04-07 10:07:36 +02:00
|
|
|
|
-r : library root folder
|
2013-03-03 00:01:20 +01:00
|
|
|
|
-t : title
|
|
|
|
|
|
2013-04-07 10:07:36 +02:00
|
|
|
|
You can use the following variables to refer to existing values:
|
2013-03-03 00:01:20 +01:00
|
|
|
|
\$ALBUM
|
2013-09-11 09:15:03 +02:00
|
|
|
|
\$ALBUMARTIST
|
2013-03-03 00:01:20 +01:00
|
|
|
|
\$ARTIST
|
|
|
|
|
\$COMPOSER
|
|
|
|
|
\$DATE
|
2013-04-07 10:07:36 +02:00
|
|
|
|
\$FILENAME
|
2013-03-03 00:01:20 +01:00
|
|
|
|
\$GENRE
|
|
|
|
|
\$TRACK
|
2013-04-07 10:07:36 +02:00
|
|
|
|
\$TYER
|
2013-03-03 00:01:20 +01:00
|
|
|
|
|
|
|
|
|
Default output folder:
|
2013-05-02 12:34:17 +02:00
|
|
|
|
OUTPUT_FOLDER="\$OUTPUT_ROOT/\$OUTPUT_ARTIST/\${OUTPUT_ALBUM:+\${OUTPUT_DATE:+\$OUTPUT_DATE - }\$OUTPUT_ALBUM/}"
|
2013-03-03 00:01:20 +01:00
|
|
|
|
|
|
|
|
|
Default output file:
|
2013-08-03 21:27:11 +02:00
|
|
|
|
OUTPUT_FILE="\$OUTPUT\$OUTPUT_ARTIST - \${OUTPUT_TRACK:+\$OUTPUT_TRACK - }\$OUTPUT_TITLE.\$OUTPUT_EXT"
|
2013-03-03 00:01:20 +01:00
|
|
|
|
|
|
|
|
|
Examples:
|
|
|
|
|
|
|
|
|
|
Set the 'artist' tag and reencode:
|
2013-03-05 21:53:29 +01:00
|
|
|
|
${1##*/} -a 'Franz Liszt' file.mp3
|
2013-03-03 00:01:20 +01:00
|
|
|
|
|
2013-03-05 21:53:29 +01:00
|
|
|
|
Set 'artist' to be 'composer', and 'title' to be preceded by 'artist', do not reencode:
|
2013-03-03 11:19:50 +01:00
|
|
|
|
${1##*/} -s -a '\$COMPOSER' -t '\$ARTIST - \$TITLE' file.ogg
|
2013-03-03 00:01:20 +01:00
|
|
|
|
|
2013-09-11 09:15:03 +02:00
|
|
|
|
Set track number to first field in file name:
|
|
|
|
|
${1##*/} -n '${FILENAME%% }'
|
|
|
|
|
|
2013-08-03 21:27:11 +02:00
|
|
|
|
IMPORTANT: you *must* use single quotes when using variables. Also note that the
|
|
|
|
|
string parameters are all evaluated using 'eval', which means that some
|
|
|
|
|
characters may need escaping with '\'. Since parenthesis '()' are often used, a
|
|
|
|
|
backslash is prepended before eval is called.
|
2013-03-03 00:01:20 +01:00
|
|
|
|
EOF
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
## OPTIONS
|
2013-05-02 12:34:17 +02:00
|
|
|
|
CAPITAL=0
|
2013-05-03 23:44:53 +02:00
|
|
|
|
OVERWRITE="-n"
|
2013-09-22 12:50:20 +02:00
|
|
|
|
PREVIEW=false
|
2013-08-03 21:27:11 +02:00
|
|
|
|
SKIP=false
|
2013-10-08 22:17:30 +02:00
|
|
|
|
LOGLEVEL=""
|
2013-03-03 00:01:20 +01:00
|
|
|
|
|
|
|
|
|
## TAGS
|
|
|
|
|
OUTPUT_ALBUM='$ALBUM'
|
|
|
|
|
OUTPUT_ARTIST='$ARTIST'
|
|
|
|
|
OUTPUT_DATE='$DATE'
|
|
|
|
|
OUTPUT_GENRE='$GENRE'
|
|
|
|
|
OUTPUT_TITLE='$TITLE'
|
|
|
|
|
OUTPUT_TRACK='$TRACK'
|
|
|
|
|
|
|
|
|
|
## PROPERTIES
|
2013-09-29 18:22:24 +02:00
|
|
|
|
OUTPUT_BITRATE=-1
|
2013-03-03 00:01:20 +01:00
|
|
|
|
|
2013-03-05 21:53:29 +01:00
|
|
|
|
## Non-CLI-option data. Modifying these imply modifications in code below.
|
2013-08-03 21:27:11 +02:00
|
|
|
|
OUTPUT_EXT="ogg"
|
2013-10-08 18:16:28 +02:00
|
|
|
|
OGG_PARAM='-c:a libvorbis -b:a ${OUTPUT_BITRATE}k'
|
2013-03-03 00:01:20 +01:00
|
|
|
|
|
2013-03-05 21:53:29 +01:00
|
|
|
|
## These ones are not CLI-options either, but this could be easily changed.
|
2013-08-03 21:27:11 +02:00
|
|
|
|
OUTPUT_FOLDER='$OUTPUT_ROOT/$OUTPUT_ARTIST${OUTPUT_ALBUM:+/${OUTPUT_DATE:+$OUTPUT_DATE - }$OUTPUT_ALBUM}'
|
|
|
|
|
OUTPUT_FILE='$OUTPUT_ARTIST - ${OUTPUT_PADDEDTRACK:+$OUTPUT_PADDEDTRACK - }$OUTPUT_TITLE'
|
2013-03-03 00:01:20 +01:00
|
|
|
|
|
2013-10-08 22:17:30 +02:00
|
|
|
|
while getopts ":a:b:cd:fg:l:n:r:t:hpsq" opt; do
|
2013-03-03 00:01:20 +01:00
|
|
|
|
case $opt in
|
|
|
|
|
|
|
|
|
|
a) OUTPUT_ARTIST=$OPTARG ;;
|
|
|
|
|
b) OUTPUT_BITRATE=$OPTARG ;;
|
|
|
|
|
d) OUTPUT_DATE=$OPTARG ;;
|
|
|
|
|
g) OUTPUT_GENRE=$OPTARG ;;
|
|
|
|
|
l) OUTPUT_ALBUM=$OPTARG ;;
|
|
|
|
|
n) OUTPUT_TRACK=$OPTARG ;;
|
2013-05-02 12:34:17 +02:00
|
|
|
|
r) OUTPUT_ROOT=$OPTARG ;;
|
2013-03-03 00:01:20 +01:00
|
|
|
|
t) OUTPUT_TITLE=$OPTARG ;;
|
|
|
|
|
|
|
|
|
|
h)
|
|
|
|
|
_printhelp "$0"
|
|
|
|
|
exit 1
|
|
|
|
|
;;
|
2013-05-02 12:34:17 +02:00
|
|
|
|
c)
|
2013-10-08 22:17:30 +02:00
|
|
|
|
CAPITAL=1 ;;
|
2013-05-02 12:34:17 +02:00
|
|
|
|
f)
|
2013-10-08 22:17:30 +02:00
|
|
|
|
OVERWRITE="-y" ;;
|
2013-03-03 00:01:20 +01:00
|
|
|
|
p)
|
2013-10-08 22:17:30 +02:00
|
|
|
|
PREVIEW=true ;;
|
2013-03-03 00:01:20 +01:00
|
|
|
|
s)
|
2013-10-08 22:17:30 +02:00
|
|
|
|
SKIP=true ;;
|
|
|
|
|
|
|
|
|
|
q)
|
|
|
|
|
LOGLEVEL="-v fatal" ;;
|
2013-03-03 00:01:20 +01:00
|
|
|
|
|
|
|
|
|
?)
|
|
|
|
|
_printhelp "$0"
|
|
|
|
|
exit 1
|
|
|
|
|
;;
|
|
|
|
|
:)
|
|
|
|
|
echo "Missing argument."
|
2013-03-03 11:19:50 +01:00
|
|
|
|
_printhelp "$0"
|
2013-03-03 00:01:20 +01:00
|
|
|
|
exit 1
|
|
|
|
|
;;
|
|
|
|
|
esac
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
shift $(($OPTIND - 1))
|
|
|
|
|
if [ $# -eq 0 ]; then
|
|
|
|
|
_printhelp "$0"
|
|
|
|
|
exit
|
|
|
|
|
fi
|
|
|
|
|
|
2013-10-08 22:17:30 +02:00
|
|
|
|
if [ -z "$(command -v ffmpeg)" ]; then
|
|
|
|
|
echo "ffmpeg required for transcoding."
|
|
|
|
|
exit
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
if [ -z "$(command -v realpath)" ]; then
|
|
|
|
|
echo "realpath required to get input file folder."
|
|
|
|
|
exit
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
OUTPUT_ROOT="$(realpath "$OUTPUT_ROOT")"
|
|
|
|
|
if [ ! -d "$OUTPUT_ROOT" ]; then
|
|
|
|
|
echo "Output folder '$OUTPUT_ROOT' does not exist."
|
|
|
|
|
exit
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
TITLECASE_SCRIPT="${0%/*}/titlecase.awk"
|
|
|
|
|
if [ ! -f "$TITLECASE_SCRIPT" ]; then
|
|
|
|
|
echo "AWK titlecase script required."
|
|
|
|
|
exit
|
|
|
|
|
fi
|
|
|
|
|
|
2013-03-03 00:01:20 +01:00
|
|
|
|
##================================================================================
|
|
|
|
|
## Get metadata.
|
|
|
|
|
STREAM=$(ffmpeg -i "$1" 2>&1)
|
|
|
|
|
|
2013-05-02 12:34:17 +02:00
|
|
|
|
if [ -z "$(echo $STREAM | grep "Stream")" ]; then
|
2013-08-03 21:51:24 +02:00
|
|
|
|
echo "ERROR: Non-audio file [$1]."
|
2013-03-03 00:01:20 +01:00
|
|
|
|
exit
|
|
|
|
|
fi
|
|
|
|
|
|
2013-05-02 12:34:17 +02:00
|
|
|
|
METADATA=$(echo "$STREAM" | sed -n '/Metadata/ ! d; /Metada/{b cont}; :cont ; {n;p;b cont}')
|
|
|
|
|
|
2013-04-07 10:07:36 +02:00
|
|
|
|
## Filename without extension nor path.
|
2013-08-03 21:27:11 +02:00
|
|
|
|
INPUT_FILE="${1%.*}"
|
|
|
|
|
INPUT_FILE="${INPUT_FILE##*/}"
|
2013-04-07 12:33:43 +02:00
|
|
|
|
## Folder of the file. Needed for cover.
|
2013-08-03 21:27:11 +02:00
|
|
|
|
INPUT_FOLDER="$(realpath "$1")"
|
|
|
|
|
INPUT_FOLDER="${INPUT_FOLDER%/*}"
|
|
|
|
|
INPUT_EXT="${1##*.}"
|
2013-08-03 21:51:24 +02:00
|
|
|
|
INPUT_BITRATE=$(echo "$STREAM" | sed -n '/Duration/ {s|.* \([[:digit:]]\+\) kb/s|\1|;p;q}')
|
2013-08-03 21:27:11 +02:00
|
|
|
|
## CODEC is unused for now.
|
|
|
|
|
# CODEC=$(echo "$STREAM" | sed -n '/Stream.*Audio:/ {s/.*Audio: \([^,]*\),.*/\1/;p}')
|
2013-03-03 00:01:20 +01:00
|
|
|
|
|
2013-08-03 21:51:24 +02:00
|
|
|
|
## Extension needs to be set in case we skip encoding so that ffmpeg will not be
|
|
|
|
|
## disturbed by unappropriate extension.
|
|
|
|
|
if $SKIP &&[ -z "$INPUT_EXT" ]; then
|
|
|
|
|
echo "ERROR: Extension missing [$1]."
|
2013-03-03 00:01:20 +01:00
|
|
|
|
exit
|
|
|
|
|
fi
|
|
|
|
|
|
2013-04-05 14:47:26 +02:00
|
|
|
|
## This function greps for one match only, so if several metadata are present,
|
|
|
|
|
## this may not be the desired values.
|
2013-03-03 00:01:20 +01:00
|
|
|
|
_metadata_filter()
|
|
|
|
|
{
|
2013-04-05 14:47:26 +02:00
|
|
|
|
echo "$METADATA" | grep -im1 "\<$1\>" | sed 's/[^:]* : //g'
|
2013-03-03 00:01:20 +01:00
|
|
|
|
}
|
|
|
|
|
|
2013-05-05 20:57:20 +02:00
|
|
|
|
INPUT_TITLE=$(_metadata_filter "title")
|
|
|
|
|
INPUT_ARTIST=$(_metadata_filter "artist")
|
|
|
|
|
INPUT_ALBUM=$(_metadata_filter "album")
|
2013-09-11 09:15:03 +02:00
|
|
|
|
INPUT_ALBUMARTIST=$(_metadata_filter "album_artist")
|
2013-05-05 20:57:20 +02:00
|
|
|
|
INPUT_COMPOSER=$(_metadata_filter "composer")
|
|
|
|
|
INPUT_DISC=$(_metadata_filter "disc")
|
|
|
|
|
INPUT_GENRE=$(_metadata_filter "genre")
|
|
|
|
|
INPUT_TRACK=$(_metadata_filter "track")
|
|
|
|
|
INPUT_DATE=$(_metadata_filter "date")
|
|
|
|
|
INPUT_TYER=$(_metadata_filter "TYER")
|
2013-03-03 00:01:20 +01:00
|
|
|
|
|
2013-05-05 20:57:20 +02:00
|
|
|
|
##==============================================================================
|
|
|
|
|
## Variable cleansing.
|
2013-03-03 00:01:20 +01:00
|
|
|
|
|
|
|
|
|
## We use the AWK script to set title case. The script contains
|
|
|
|
|
## exceptions that can be configured. We fix some chars with sed.
|
|
|
|
|
# ’ => '
|
|
|
|
|
# : => -
|
|
|
|
|
# / => -
|
|
|
|
|
_string_cleanser()
|
|
|
|
|
{
|
2013-05-02 12:34:17 +02:00
|
|
|
|
echo "$@" | awk -v capital=$CAPITAL -f "$TITLECASE_SCRIPT" \
|
2013-03-03 00:01:20 +01:00
|
|
|
|
| sed -n -e "s/’/'/g ; s/ *: */ - /g ; s| */ *| - |g; p; q"
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-03 21:27:11 +02:00
|
|
|
|
## These are the user-accessible variables.
|
2013-05-05 20:57:20 +02:00
|
|
|
|
TITLE=$(_string_cleanser "$INPUT_TITLE")
|
|
|
|
|
ARTIST=$(_string_cleanser "$INPUT_ARTIST")
|
|
|
|
|
ALBUM=$(_string_cleanser "$INPUT_ALBUM")
|
2013-09-11 09:15:03 +02:00
|
|
|
|
ALBUMARTIST=$(_string_cleanser "$INPUT_ALBUMARTIST")
|
2013-05-05 20:57:20 +02:00
|
|
|
|
COMPOSER=$(_string_cleanser "$INPUT_COMPOSER")
|
|
|
|
|
DISC=$(_string_cleanser "$INPUT_DISC")
|
|
|
|
|
GENRE=$(_string_cleanser "$INPUT_GENRE")
|
|
|
|
|
TRACK=$(_string_cleanser "$INPUT_TRACK")
|
|
|
|
|
DATE=$(_string_cleanser "$INPUT_DATE")
|
|
|
|
|
TYER=$(_string_cleanser "$INPUT_TYER")
|
2013-09-11 09:15:03 +02:00
|
|
|
|
|
|
|
|
|
FILENAME=$(_string_cleanser "$INPUT_FILE")
|
2013-03-03 00:01:20 +01:00
|
|
|
|
|
2013-05-05 20:57:20 +02:00
|
|
|
|
##================================================================================
|
|
|
|
|
## OUTPUT variables.
|
2013-03-03 00:01:20 +01:00
|
|
|
|
|
2013-09-08 11:56:55 +02:00
|
|
|
|
## In this part we prepend backslashes to parentheses when there is an 'eval'
|
2013-08-03 21:27:11 +02:00
|
|
|
|
## call to prevent parsing error.
|
|
|
|
|
OUTPUT_TITLE=$(eval echo $(echo ${OUTPUT_TITLE:-Unknown Title} | sed 's/[()]/\\&/g'))
|
|
|
|
|
OUTPUT_ALBUM=$(eval echo $(echo ${OUTPUT_ALBUM:-Unknown Album} | sed 's/[()]/\\&/g'))
|
2013-05-05 20:57:20 +02:00
|
|
|
|
|
|
|
|
|
## We use album artist if artist is empty.
|
2013-09-11 09:15:03 +02:00
|
|
|
|
[ -z "$OUTPUT_ARTIST" ] && [ -n "$ALBUMARTIST" ] && OUTPUT_ARTIST="$ALBUMARTIST"
|
2013-08-03 21:27:11 +02:00
|
|
|
|
OUTPUT_ARTIST=$(eval echo $(echo ${OUTPUT_ARTIST:-Unknown Artist} | sed 's/[()]/\\&/g'))
|
2013-03-03 00:01:20 +01:00
|
|
|
|
|
2013-09-08 11:56:55 +02:00
|
|
|
|
## If OUTPUT_GENRE is set from command-line parameters, we use the
|
2013-04-07 12:33:43 +02:00
|
|
|
|
## string. Otherwise we put GENRE in lower case and underscore to ease matching.
|
|
|
|
|
## If it matches, we use the Title Case match. If it does not, we set it to
|
|
|
|
|
## empty.
|
2013-09-08 11:56:55 +02:00
|
|
|
|
if [ ! "$OUTPUT_GENRE" = '$GENRE' ]; then
|
|
|
|
|
OUTPUT_GENRE=$(eval echo $(echo $OUTPUT_GENRE | sed 's/[()]/\\&/g'))
|
2013-05-05 20:57:20 +02:00
|
|
|
|
else
|
2013-09-08 11:56:55 +02:00
|
|
|
|
## We also convert spaces to underscores.
|
2013-05-05 20:57:20 +02:00
|
|
|
|
GENRE=$(echo "$GENRE" | tr '[:upper:] ' '[:lower:]_')
|
2013-04-07 12:33:43 +02:00
|
|
|
|
case $GENRE in
|
|
|
|
|
ost) OUTPUT_GENRE="Soundtrack" ;;
|
|
|
|
|
soundtrack) OUTPUT_GENRE="Soundtrack";;
|
|
|
|
|
original_soundtrack) OUTPUT_GENRE="Soundtrack";;
|
|
|
|
|
classical) OUTPUT_GENRE="Classical";;
|
|
|
|
|
classics) OUTPUT_GENRE="Classical";;
|
|
|
|
|
classic) OUTPUT_GENRE="Classical";;
|
|
|
|
|
humour) OUTPUT_GENRE="Humour";;
|
|
|
|
|
*) OUTPUT_GENRE="";;
|
|
|
|
|
esac
|
|
|
|
|
fi
|
|
|
|
|
|
2013-04-05 14:47:26 +02:00
|
|
|
|
## We remove the track count if any, we suppress leading zeros, we suppress all
|
|
|
|
|
## non-digit characters.
|
2013-08-03 21:27:11 +02:00
|
|
|
|
OUTPUT_TRACK=$(eval _string_cleanser $(echo $OUTPUT_TRACK | sed 's/[()]/\\&/g') | sed -e 's/^0*//' -e 's|[^[:digit:]].*||')
|
2013-03-03 00:01:20 +01:00
|
|
|
|
|
2013-04-21 11:24:27 +02:00
|
|
|
|
## We extract the four-digits number from the date.
|
2013-08-03 21:27:11 +02:00
|
|
|
|
OUTPUT_DATE=$(eval _string_cleanser $(echo $OUTPUT_DATE | sed 's/[()]/\\&/g'))
|
2013-04-21 11:24:27 +02:00
|
|
|
|
OUTPUT_DATE=$(echo "$OUTPUT_DATE" | sed -n 's/.*\([[:digit:]]\{4\}\).*/\1/p')
|
|
|
|
|
## If DATE is not a year, we use TYER if it is a year.
|
2013-03-03 00:01:20 +01:00
|
|
|
|
TYER_REG=$(echo "$TYER" | sed -n 's/.*\([[:digit:]]\{4\}\).*/\1/p')
|
|
|
|
|
if [ ${#DATE} -ne 4 ] && [ ${#TYER_REG} -eq 4 ]; then
|
|
|
|
|
OUTPUT_DATE="$TYER_REG"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
## QUALITY
|
2013-08-03 21:51:24 +02:00
|
|
|
|
## If bitrate argument is not provided, we use INPUT_BITRATE value.
|
2013-09-29 18:22:24 +02:00
|
|
|
|
## If bitrate argument is 0, we leave FFmpeg chose the value.
|
|
|
|
|
[ $OUTPUT_BITRATE -lt 0 ] && OUTPUT_BITRATE=${INPUT_BITRATE}
|
|
|
|
|
[ $OUTPUT_BITRATE -eq 0 ] && OGG_PARAM="-c:a libvorbis"
|
2013-04-21 11:24:27 +02:00
|
|
|
|
## If OUTPUT_BITRATE is beyond OGG's limit, we trim it.
|
|
|
|
|
[ $OUTPUT_BITRATE -gt 500 ] && OUTPUT_BITRATE=500
|
|
|
|
|
|
2013-03-03 00:01:20 +01:00
|
|
|
|
|
|
|
|
|
## Only reencode if not in OGG and if SKIP not set.
|
2013-08-03 21:51:24 +02:00
|
|
|
|
INPUT_EXT_LOW="$(echo $INPUT_EXT | tr [:upper:] [:lower:])"
|
2013-08-03 21:27:11 +02:00
|
|
|
|
if $SKIP; then
|
|
|
|
|
OGG_PARAM="-c:a copy"
|
2013-08-03 21:51:24 +02:00
|
|
|
|
OUTPUT_EXT="$INPUT_EXT_LOW"
|
2013-08-03 21:27:11 +02:00
|
|
|
|
fi
|
2013-08-03 21:51:24 +02:00
|
|
|
|
[ "$INPUT_EXT_LOW" = "ogg" ] && OGG_PARAM="-c:a copy"
|
2013-10-08 18:16:28 +02:00
|
|
|
|
OGG_PARAM=$(eval echo "$OGG_PARAM")
|
2013-03-03 00:01:20 +01:00
|
|
|
|
|
|
|
|
|
## Make sure track number has two digits for file name only.
|
|
|
|
|
OUTPUT_PADDEDTRACK=$OUTPUT_TRACK
|
|
|
|
|
if [ -n "$OUTPUT_PADDEDTRACK" ]; then
|
|
|
|
|
[ ${OUTPUT_PADDEDTRACK} -lt 10 ] && OUTPUT_PADDEDTRACK="0$OUTPUT_PADDEDTRACK"
|
|
|
|
|
fi
|
|
|
|
|
|
2013-05-02 12:34:17 +02:00
|
|
|
|
OUTPUT_FOLDER=$(eval echo $OUTPUT_FOLDER)
|
2013-03-03 00:01:20 +01:00
|
|
|
|
OUTPUT_FILE=$(eval echo $OUTPUT_FILE)
|
2013-08-03 21:27:11 +02:00
|
|
|
|
unset OUTPUT_FILE_ORIGINAL
|
2013-03-03 00:01:20 +01:00
|
|
|
|
|
2013-08-03 21:27:11 +02:00
|
|
|
|
if [ -e "$OUTPUT_FOLDER/$OUTPUT_FILE.$OUTPUT_EXT" ]; then
|
2013-05-03 23:44:53 +02:00
|
|
|
|
if [ $OVERWRITE = "-n" ]; then
|
2013-05-02 12:34:17 +02:00
|
|
|
|
## If file exist, we append a unique timestamp to the name.
|
|
|
|
|
OUTPUT_FILE="$OUTPUT_FILE-$(date '+%F-%H%M%S')"
|
|
|
|
|
OUTPUT_MSG="$(tput setf 1)$(tput bold)(Warning: destination exists, appending timestamp.)$(tput sgr0)"
|
|
|
|
|
else
|
2013-08-03 22:17:26 +02:00
|
|
|
|
## WARNING: here it is important that no folder are suffixed by slashes.
|
|
|
|
|
if [ "$INPUT_FOLDER/$INPUT_FILE.$INPUT_EXT" = "$OUTPUT_FOLDER/$OUTPUT_FILE.$OUTPUT_EXT" ]; then
|
2013-08-03 21:27:11 +02:00
|
|
|
|
OUTPUT_FILE_ORIGINAL="$OUTPUT_FILE"
|
|
|
|
|
OUTPUT_FILE="$OUTPUT_FILE-$(date '+%F-%H%M%S')"
|
|
|
|
|
fi
|
2013-05-02 12:34:17 +02:00
|
|
|
|
OUTPUT_MSG="$(tput setf 4)$(tput bold)(Warning: overwriting destination!)$(tput sgr0)"
|
|
|
|
|
fi
|
|
|
|
|
fi
|
2013-03-03 00:01:20 +01:00
|
|
|
|
|
|
|
|
|
##==============================================================================
|
|
|
|
|
## PREVIEW
|
|
|
|
|
|
2013-09-22 12:50:20 +02:00
|
|
|
|
## Note: most (all?) shell printf have an alignment issue when strings contain
|
2013-10-08 22:17:30 +02:00
|
|
|
|
## wide characters. We need to use AWK for proper alignment. Hence the 'aprint'
|
|
|
|
|
## function.
|
2013-09-22 12:50:20 +02:00
|
|
|
|
|
2013-08-03 22:08:12 +02:00
|
|
|
|
ATTR_WIDTH="%-13.13s" # Length of longest attribute +2
|
|
|
|
|
## INPUT_WIDTH = COLUNMS - ATTR_WIDTH -2 (for |))
|
|
|
|
|
INPUT_WIDTH=$((($(tput cols)-15)/2))
|
|
|
|
|
INPUT_WIDTH="%$INPUT_WIDTH.${INPUT_WIDTH}s"
|
2013-08-03 21:27:11 +02:00
|
|
|
|
|
2013-10-22 20:00:37 +02:00
|
|
|
|
## We output everything in one pass to speed up the process since this is quite
|
|
|
|
|
## demanding and called frequently. This function is reliable as long as no
|
|
|
|
|
## tabs are found in tags. But since we have no control over the input, we never
|
|
|
|
|
## no.
|
2013-09-22 12:50:20 +02:00
|
|
|
|
aprint()
|
|
|
|
|
{
|
2013-10-22 20:00:37 +02:00
|
|
|
|
awk -F'\t+' -v FMT="$INPUT_WIDTH | $ATTR_WIDTH| %s\n" '{printf FMT, $1, $2, $3 }'
|
2013-09-22 12:50:20 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
aprint <<EOF
|
2013-10-22 20:00:37 +02:00
|
|
|
|
:: INTPUT :: :: OUTPUT ::
|
|
|
|
|
[$INPUT_ARTIST] Artist [$OUTPUT_ARTIST]
|
|
|
|
|
[$INPUT_ALBUM] Album [$OUTPUT_ALBUM]
|
|
|
|
|
[$INPUT_TRACK] Track [$OUTPUT_TRACK]
|
|
|
|
|
[$INPUT_TITLE] Title [$OUTPUT_TITLE]
|
|
|
|
|
[$INPUT_DATE] Date [$OUTPUT_DATE]
|
|
|
|
|
[$INPUT_GENRE] Genre [$OUTPUT_GENRE]
|
|
|
|
|
[$INPUT_EXT] Ext [$OUTPUT_EXT]
|
|
|
|
|
[$INPUT_BITRATE] Bitrate [$OUTPUT_BITRATE]
|
|
|
|
|
[$INPUT_ALBUMARTIST] Albumartist
|
|
|
|
|
[$INPUT_COMPOSER] Composer
|
|
|
|
|
[$INPUT_DISC] Disc
|
|
|
|
|
[$INPUT_TYER] Tyer
|
2013-09-22 12:50:20 +02:00
|
|
|
|
EOF
|
|
|
|
|
|
|
|
|
|
cat <<EOF
|
2013-08-03 21:27:11 +02:00
|
|
|
|
:: OUTPUT $OUTPUT_MSG
|
|
|
|
|
[$OUTPUT_FOLDER/$OUTPUT_FILE.$OUTPUT_EXT]
|
|
|
|
|
EOF
|
2013-03-03 00:01:20 +01:00
|
|
|
|
|
2013-09-22 12:50:20 +02:00
|
|
|
|
$PREVIEW && exit
|
2013-03-03 00:01:20 +01:00
|
|
|
|
|
|
|
|
|
##==============================================================================
|
|
|
|
|
## RUN PROCESS
|
|
|
|
|
|
|
|
|
|
## Make sure directory exists.
|
2013-05-02 12:34:17 +02:00
|
|
|
|
mkdir -p "$OUTPUT_FOLDER"
|
2013-03-03 00:01:20 +01:00
|
|
|
|
if [ $? -ne 0 ]; then
|
2013-03-05 21:53:29 +01:00
|
|
|
|
echo "ERROR: could not create output folder [$OUTPUT]."
|
2013-03-03 00:01:20 +01:00
|
|
|
|
exit
|
|
|
|
|
fi
|
|
|
|
|
|
2013-04-07 12:33:43 +02:00
|
|
|
|
## COVER. We copy cover only if it does not already exist. Only files found in
|
|
|
|
|
## the folder where the music is located will be taken into account.
|
2013-03-03 00:01:20 +01:00
|
|
|
|
while read -r i; do
|
2013-05-05 20:57:20 +02:00
|
|
|
|
[ -z "$i" ] && break
|
|
|
|
|
OUTPUT_COVER="$OUTPUT_FOLDER/${OUTPUT_ALBUM:+$OUTPUT_ALBUM - }Cover"
|
2013-03-03 00:01:20 +01:00
|
|
|
|
OUTPUT_COVERFILE="$OUTPUT_COVER.${i##*.}"
|
|
|
|
|
COVER_COUNTER=1
|
|
|
|
|
|
|
|
|
|
## Same cover is already in target folder.
|
|
|
|
|
if [ -e "$OUTPUT_COVERFILE" ] && \
|
2013-03-05 21:53:29 +01:00
|
|
|
|
[ "$(sha1sum "$OUTPUT_COVERFILE" | cut -f1 -d' ')" = "$(sha1sum "$i" | cut -f1 -d' ')" ]; then
|
2013-03-03 00:01:20 +01:00
|
|
|
|
continue
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
## Different cover with same name is in target folder. We append a number.
|
|
|
|
|
while [ -e "$OUTPUT_COVERFILE" ] && \
|
2013-03-05 21:53:29 +01:00
|
|
|
|
[ ! "$(sha1sum "$OUTPUT_COVERFILE" | cut -f1 -d' ')" = "$(sha1sum "$i" | cut -f1 -d' ')" ]; do
|
2013-03-03 00:01:20 +01:00
|
|
|
|
OUTPUT_COVERFILE="${OUTPUT_COVER} $COVER_COUNTER.${i##*.}"
|
|
|
|
|
COVER_COUNTER=$(($COVER_COUNTER+1))
|
|
|
|
|
done
|
|
|
|
|
|
2013-08-03 21:27:11 +02:00
|
|
|
|
echo ":: COVER [$i] from $INPUT_FOLDER"
|
2013-03-03 00:01:20 +01:00
|
|
|
|
cp -nv "$i" "$OUTPUT_COVERFILE"
|
|
|
|
|
echo
|
|
|
|
|
done <<EOF
|
2013-08-03 21:27:11 +02:00
|
|
|
|
$(find "$INPUT_FOLDER" -maxdepth 1 \( -iname '*.png' -o -iname '*.jpg' \) )
|
2013-03-03 00:01:20 +01:00
|
|
|
|
EOF
|
|
|
|
|
|
|
|
|
|
## Zsh compatibility. We need it otherwise word splitting of parameter like
|
|
|
|
|
## OGG_PARAM will not work.
|
|
|
|
|
STATUS="$(set -o | grep 'shwordsplit' | awk '{print $2}')"
|
|
|
|
|
[ "$STATUS" = "off" ] && set -o shwordsplit
|
|
|
|
|
|
|
|
|
|
## TAG/RECODE
|
2013-04-21 11:24:27 +02:00
|
|
|
|
## With the -map_metadata parameter we clear all metadata.
|
2013-10-08 22:17:30 +02:00
|
|
|
|
echo ":: Processing..."
|
|
|
|
|
ffmpeg $LOGLEVEL $OVERWRITE -i "$1" -vn -sn $OGG_PARAM \
|
2013-04-21 11:24:27 +02:00
|
|
|
|
-map_metadata -1 \
|
2013-03-03 00:01:20 +01:00
|
|
|
|
-metadata title="$OUTPUT_TITLE" \
|
|
|
|
|
-metadata artist="$OUTPUT_ARTIST" \
|
|
|
|
|
-metadata track="$OUTPUT_TRACK" \
|
|
|
|
|
-metadata date="$OUTPUT_DATE" \
|
|
|
|
|
-metadata album="$OUTPUT_ALBUM" \
|
|
|
|
|
-metadata album_artist="$OUTPUT_ARTIST" \
|
|
|
|
|
-metadata genre="$OUTPUT_GENRE" \
|
2013-08-03 21:27:11 +02:00
|
|
|
|
"$OUTPUT_FOLDER/$OUTPUT_FILE.$OUTPUT_EXT"
|
2013-10-08 22:17:30 +02:00
|
|
|
|
echo ":: Process finished!"
|
2013-03-03 00:01:20 +01:00
|
|
|
|
|
2013-08-03 21:27:11 +02:00
|
|
|
|
## If we are overwriting inplace.
|
|
|
|
|
if [ -n "$OUTPUT_FILE_ORIGINAL" ]; then
|
|
|
|
|
mv -f "$OUTPUT_FOLDER/$OUTPUT_FILE.$OUTPUT_EXT" "$OUTPUT_FOLDER/$OUTPUT_FILE_ORIGINAL.$OUTPUT_EXT"
|
|
|
|
|
fi
|
2013-05-02 12:34:17 +02:00
|
|
|
|
echo
|
|
|
|
|
|
2013-03-03 00:01:20 +01:00
|
|
|
|
## Restore Zsh previous options. This will not turn off shwordsplit if it
|
|
|
|
|
## was on before calling the function.
|
|
|
|
|
[ "$STATUS" = "off" ] && set +o shwordsplit
|