Creating Playlists for my MP3 Collection

As I’ve mentioned before, about two summers back I went through my whole CD collection and digitized it to MP3 format using a tool called ABCDE. What I didn’t mention in that post was that at the time I’d forgotten to create playlists automatically during the process. Bummer! I had all of these albums digitized, but whenever I wanted to play a particular album I’d have to add the folder through my music player or manually select all of the files to add to my playlist. That wasn’t too bad, but if you’ve had any experience with playlists in Windows Media Player, VLC, or even XMMS you’ll know that in practice it is just a pain to handle. For whatever reason it may be, the ordering of the album tracks on the playlist always gets messed up when adding manually or via folders. Always! Now, I know I can fix this simply by sorting by the ID3 metadata (click the “track number” column in most players to sort), but the point is that I shouldn’t have to do this. I wanted a simple solution.

I get annoyed with these things pretty quickly, so I started thinking about how I could remedy the situation to save myself some time. Obviously I could investigate why each of the players were failing to sort my properly named (and usually tagged) files, but that seemed like too much work. I thought a simple solution would be to create M3U (a standard playlist format) playlist files in each album directory that would contain the exact track ordering of that particular album. That way to play an album all I’d need to do was double click on the playlist file and voila! it would play it in the right order. This would also give me the ability to drag and drop multiple albums without having to worry about sort issues with having multiple track 1s (not too much of a problem with today’s players, but still).

I did a little research and found a tool to help me write the playlists. It’s a Python library called mutagen, and it basically allowed me to programmatically read the metadata of Mp3 files to get the following information (among the many other things it can retrieve):

  • Track number
  • Artist name
  • Album name
  • Track length (seconds)
After installing mutagen, I wrote the following Python script (m3u.py) to traverse through a set of directories and create playlist files at the lowest level. For example, if I ran it on /home/ant/Music/Coldplay/Parachutes I would end up with a playlist file inside the Parachutes directory:

[python] #!/usr/bin/python

import os import sys import glob from mutagen.mp3 import MP3 from mutagen.easyid3 import EasyID3

def makem3u(dir="."): try: print "Processing directory ‘%s’." % dir os.chdir(dir)

    # get ID3 meta objects for each mp3,
    # store in a list
    playlist = ''
    mp3s = []
    for file in glob.glob("*.[mM][pP]3"):
        if playlist == '':
            playlist = EasyID3(file)['album'][0] + '.m3u'
        meta_info = {
            'filename': file,
            'length': int(MP3(file).info.length),
            'tracknumber': EasyID3(file)['tracknumber'][0].split('/')[0],
        }
        mp3s.append(meta_info)

    if len(mp3s) > 0:
        print "Writing playlist %s." % playlist

        # write the playlist
        of = open(playlist, 'w')
        of.write("#EXTM3U\n")

        # sorted by track number
        for mp3 in sorted(mp3s, key=lambda mp3: int(mp3['tracknumber'])):
            of.write("#EXTINF:%s,%s\n" % (mp3['length'], mp3['filename']))
            of.write(mp3['filename'] + "\n")

        of.close()

except:
    print "Error when trying to process directory '%s'. Ignoring..." % dir
    print "Text:", sys.exc_info()[0]

def main(argv = None): if argv is None: argv = sys.argv

# directories containing music files
dirs = []

if len(sys.argv) == 2 and sys.argv[1] == '-':
# we do not have command line arguments,
# so read from STDIN
   for line in sys.stdin:
       dirs.append(line.strip())
else:
# passed in directories on the command line
    for dir in sys.argv[1:]:
        dirs.append(dir)

# for each directory passed to us, go
# to it and make the M3U out of the
# MP3 files there
for dir in dirs:
    makem3u(dir)

return 0

if name == "main": sys.exit(main()) [/python]

You can pass directories two ways this script - as arguments or via standard input. This allows me to do this:

m3u.py /home/ant/Music/Coldplay/Parachutes

…or if I wanted to do a bunch of directories in bulk:

find /home/ant/Music -type d -links 2 | m3u.py - 

Note that I made only a small attempt to make this script bulletproof. It gives a decent attempt to find the metadata in the MP3 files it finds in the directory, but if it encounters an error thrown by mutagen it dies out with an error message telling me the directory that failed and the exception it hit. Nine times out of ten (in my testing) the script will die because the tag information in the MP3s are malformed, which means that you wouldn’t want to create a playlist off of them using that info anyway.