Sunday, February 7, 2010

Mass-(re)naming files from .flac tags

The story is usually the other way around: you have a bunch of correctly named audio files and you want to automatically mass-tag them. Most players and tag editors already allow you to do that.

My problem was the exact reverse of this situation: I had neatly tagged .flac-s and I wanted to rename the files, which were bleakly called split-track01.flac, split-track02.flac etc.

I came up with a handy script that allows me to do just that. In order for it to work, you will of course need the FLAC tools:

sudo aptitude install flac

Note that this script works only for .flac files at the moment (I have not yet taken the time to dig into the differences between ID3v1 and ID3v2 tags for .mp3-s, nor did I figure out how is it that I should decide on which tag version to take into account – in case both are present).

Before proceeding with the actual script, let me explain a bit what it does and how it should be used. Suppose you have a directory with these files (all with their tags in place):

alex@enterprise$ ls -1

You know these files are part of the same album and you want to rename them, according to their tags. Here's what the script does if it is invoked with the --display argument:

alex@enterprise$ track_number=1 ; for track in cd7split-track05.flac cd8split-track01.flac cd8split-track02.flac ; do ./ $track $track_number --display ; track_number=$(($track_number+1)) ; done
cd7split-track05.flac: 01_Anton_Bruckner_-_Symphony_No._9_in_D_minor_-_I._Feierlich._Misterioso.flac
cd8split-track01.flac: 02_Anton_Bruckner_-_Symphony_No._9_in_D_minor_-_II._Scherzo._Bewegt_lebhaft_-_Trio._Schnell.flac
cd8split-track02.flac: 03_Anton_Bruckner_-_Symphony_No._9_in_D_minor_-_III._Adagio._Langsam_feierlich.flac

As you can see, the second argument (the one between the file name and the optional --display) represents the track number that is to be inserted at the beginning of the file name. The above output is just that, an output. No changes have been made upon the files themselves. So now that you've checked and made sure there is no error in the file naming scheme, you can run it without the --display parameter. The files will be renamed:

alex@enterprise$ track_number=1 ; for track in cd7split-track05.flac cd8split-track01.flac cd8split-track02.flac ; do ./ $track $track_number ; track_number=$(($track_number+1)) ; done
alex@enterprise$ ls -1

That was the usage part. Here is a short description of what the script actually does:

  • If the script is called with anything but two or three arguments: a usage reminder is displayed;
  • If it is called with three arguments but the third one is different from --display: a usage reminder is displayed;
  • If the second argument (the track number) is not a number or if it is a number less than 1 (i.e. zero or negative): an error message and a usage reminder are displayed;
  • If the file specified by the first argument does not exist: an error message is displayed;
  • If the file specified by the first argument is not a valid .flac: an error message is displayed;
  • The values of the artist, album and track title tags are stored in local variables. The values stored in these variables have two characteristics: all spaces are turned into underscores ('_') and other special characters (that should not appear in a file name) are suppressed;
  • The new file name is finally constructed from the said variables. If the --display argument is called, the script will only show what the file name(s) would look like, without doing the actual renaming. If the --display argument is not invoked, the renaming will proceed normally.

These being said, here's the script:

# Return codes
# 0 - success
# 1 - incorrect usage
# 2 - incorrect <track_number> argument
# 3 - file <file_name.flac> not found
# 4 - file <file_name.flac> not .flac

function usage() {
echo "Usage: $0 <file_name.flac> <track_number> [--display]"
echo ""
echo " e.g. The command "
echo " $0 split-track01.flac 3 "
echo " will read the tags in 'split-track01.flac' and will rename "
echo " the file accordingly, preceding it by the specified track "
echo " number – '03' in this case."
echo ""
echo "--display Use this if you only wish to check the resulting "
echo " file name, without actually renaming the file."
echo " e.g. $0 split-track01.flac 3 --display"

function display() {
echo "$1: $2"
exit 0

if [ $# -ne 2 ]
if [ $# -ne 3 ]
exit 1
if [ "$3" != "--display" ]
exit 1

if [ "$( expr $2 - $2 2>/dev/null )" != "0" -o $2 -lt 1 ]
echo "The <track_number> must be a numeric, greater than zero value!"
exit 2

if [ ! -e "$1" ]
echo "The file you specified does not exist."
exit 3

if [ "$( file "$1" | grep -c "FLAC audio bitstream data" )" -eq 0 ]
echo "The file you specified is not a valid FLAC."
exit 4

flac_artist="$( metaflac --show-tag=artist "$1" | awk -F= '{ print $2 }' )"
flac_artist="$( echo "$flac_artist" | sed -e 's/\ /_/g' | sed -e "s/'//g" )"
flac_artist="$( echo "$flac_artist" | sed -e 's/[|\/\\",;:?!%&]//g' )"

flac_album="$( metaflac --show-tag=album "$1" | awk -F= '{ print $2 }' )"
flac_album="$( echo "$flac_album" | sed -e 's/\ /_/g' | sed -e "s/'//g" )"
flac_album="$( echo "$flac_album" | sed -e 's/[|\/\\",;:?!%&]//g' )"

flac_title="$( metaflac --show-tag=title "$1" | awk -F= '{ print $2 }' )"
flac_title="$( echo "$flac_title" | sed -e 's/\ /_/g' | sed -e "s/'//g" )"
flac_title="$( echo "$flac_title" | sed -e 's/[|\/\\",;:?!%&]//g' )"

if [ $2 -ge 10 ]


if [ "$3" == "--display" ]
display "$1" "$flac_name"

# if the execution has reached this line, it means the '--display' argument
# was not used; proceed with renaming the file

mv "$1" "$flac_name"

exit 0


Post a Comment