TheGeekery

The Usual Tech Ramblings

Bash Magic...

A while ago, I posted an quick item on normalization of my music collection. What it left me with, was a directory full of all the music files, and no particular layout at all. I’m particularly fond of trying to keep my directories tidy, no mater what operating system, so this was beginning to bug me just a little bit. My standard format is “/media/artist/album/”. Sometimes I will also use “/media/artist/album/track# – Title” so that they are in order by their track number. As they were all lumped into one folder with the file naming convention of “artist – title”, this would require just a little bit of work, or a little bash magic.

First, I need to find out the album names. As this isn’t part of the file name, I had to use something else. mp3info is handy at this as it can read the MP3 tags. So to get just the album name, I call:

1
mp3info -p "%l" <file>

This returns the album name. mp3info isn’t just limited to the album, but can return any of the MP3 ID tags. The other really handy thing about mp3info is the -p option behaves like printf. With that in mind, I can now use the following to pre-build a directory structure:

1
mp3info -p "/media/%a/%l" <file>

This should output something like /media/Finger Eleven/Them Vs You Vs Me for the file Finger Eleven – Lost My Way.mp3. Now I have a directory structure, I need to build a new filename to fit my preferred format of “track# – title”. This again calls mp3info

1
mp3info -p "%02n - %t.mp3" <file>

This will return “04 – Lost My Way” for the same track above. Here is where the bash magic binds it all together. First we have to go through all the MP3 files to get the information. That’s a case of a simple loop.

1
2
3
4
for file in *.mp3;
do
  [..]
done

Then, within that loop, we have to mash together all the previously extracted information. We’ll drop them into handy variables as we go through the loop:

1
2
  OUTDIR=`mp3info -p "/media/%a/%l" "${file}"`
  OUTTRK=`mp3info -p "%02n - %t.mp3" "${file}"`

Notice I have enclosed ${file} in double quotes. This is because I have files with spaces in them. If I didn’t, mp3info would complain about not being able to find the file “Finger”. If you don’t know why I used “%02n” for the track number, instead of just %n, see the man page for printf. It basically forces everything to a 2 digit number, with 0 padding from the left. So 1 becomes 01, 5 becomes 05, and 10 stays as 10. I do this because windows still doesn’t sort numerics properly, and treats them as text, so you end up with a stupid sort order like this:

1
10
11
2
3
4

Now we have to introduce some basic tests. We don’t want to attempt to try moving a file into a location that doesn’t exist. So we test for the existence of a directory structure:

1
2
3
4
  if [ ! -d "${OUTDIR}" ]
  then
    mkdir -p "${OUTDIR}"
  fi

Some diehardiests will probably complain at me for using spaces in my directory names, but I don’t personally have an issue with it. If you don’t like it, use some regex to change a space to an _. The -p option for mkdir forces it to create the entire tree, so if the artist folder didn’t exist, it’d build that as well as the album folder.

The final step, is to rope it all together, with the move, and you have the following:

1
2
3
4
5
6
7
8
9
10
11
12
for file in *.mp3;
do
  OUTDIR=`mp3info -p "/media/%a/%l" "${file}"`
  OUTTRK=`mp3info -p "%02n - %t.mp3" "${file}"`

  if [ ! -d "${OUTDIR}" ]
  then
    mkdir -p "${OUTDIR}"
  fi

  mv "${file}" "${OUTDIR}/${OUTTRK}"
done

This is a basic bash script, and could probably do with some more tests, such as validating the track name, and ensuring the directory was made, but I knew all my files were okay, and that I had the ability to create the directory structures. Now, my MP3s are all tidy, in a nice folder structure, and easy to track down.

Comments