Wednesday 5 November 2003 — This is 21 years old. Be careful.
On Sunday I wrote about Reading mp3 metadata, in an effort to create m3u playlist files for mp3 files stored in directories by artist and album. I got an unexpected solution from an unexpected source.
I had cast the problem as how to read data out of mp3 files so that I could populate m3u files. You see, there’s no spec I could find for m3u files, so the best I could do was to create files like those created by Winamp. They look kind of like this:
#EXTM3U
#EXTINF:173,Aimee Mann - One
\music\mp3\Aimee Mann\Magnolia\01-One.mp3
#EXTINF:207,Aimee Mann - Momentum
\music\mp3\Aimee Mann\Magnolia\02-Momentum.mp3
The number after #EXTINF is the duration of the song, in seconds. The rest of the line is the title to display in the player. So I figured I needed to determine the length of the song, and find a good title. But the title data is dirty and stored in discouragingly diverse ways. The length isn’t stored at all, and seems to require reading all of the song data to decode fiddly bit rates. Yuck.
Then I got an email on the topic from Martin Fowler of all people. He wasn’t delivering some deep insight about refactoring or object orientation. He told me about how he’s organized his mp3 files, and in particular, he told me he makes m3u files that are just a long list of file names.
Of course! Just make a list of file names, and Winamp will read the other data itself when the song plays. Simple. A few minutes with Python, and I had it all done:
# Create a tree of .m3u files, one for each directory in the tree,
# containing all the .mp3 files in the subtree rooted there.
import path
# A stack of .m3u files.
m3us = []
def doDir(d):
'''Called recursively for each directory in the tree.'''
# Make a new .m3u file for this directory.
m3u = d / (d.name + '.m3u')
m3us.append(open(m3u, 'w'))
# Add all the .mp3's in this dir to all the .m3u's.
for f in d.files('*.mp3'):
for m3ufile in m3us:
m3ufile.write(f + '\n')
# Recurse into all the subdirectories.
for dchild in d.dirs():
doDir(dchild)
# We're done with this directory's .m3u file.
m3us.pop()
doDir(path.path('.').abspath())
Thanks, Martin!
Update: In January 2004 I finally got a complete solution together.
Comments
MP3 PlayList v1.2
Found this, and thought I would give it a whirl.
It seems to puke on certain characters in dir names (on Win XP).
This is the error message I got
-----------------------------------
Traceback (most recent call last):
File "E:\m3u.py", line 30, in ?
doDir(path.path('.').abspath())
File "E:\m3u.py", line 25, in doDir
doDir(dchild)
File "E:\m3u.py", line 25, in doDir
doDir(dchild)
File "E:\m3u.py", line 25, in doDir
doDir(dchild)
File "E:\m3u.py", line 25, in doDir
doDir(dchild)
File "E:\m3u.py", line 21, in doDir
m3ufile.write(f + '\n')
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe8' in position 60: ordinal not in range(128)
-------------------------
Feedback appreciated.
import path
.
.
doDir(path.path('.').abspath())
There is no such module as path, so I modified mine to
import os.path as ospath
.
.
doDir(ospath.abspath('.'))
But now I think your function is expecting 'd' to be a type other than str.
I think this because the '/' operator fails and the .name and .files functions also fail (if I remove the '/').
I could not find what module or whatever contained these attributes (name and files)?
jorendorff.com
Add a comment: