Python script to add a file to a playlist

I have a number of playlists on Gondolin, which is a headless machine. I wanted to be able to easily add a given mp3 file to the playlists which are in m3u format. That means that each entry has both the filename and an extended line with some basic metadata, in particular the track length in seconds, the track artist and name. I wanted a script that could extract this information from the mp3 file and make adding the entry easy. So I wrote this in Python. It’s rough and ready and it is probably not very Pythonic but it’s working for me. The script should create a playlist if it doesn’t currently exist, and check for a newline at the end of the file so that the appended lines are really on a new line. ItWorksForMe (TM).

This uses the eyeD3 Python library, which on Debian is provided in python-eyed3.

My basic usage is

playlist-append -m the_mp3_file.mp3 -p the_playlist.m3u -r /var/media/mp3

the last parameter is the path relative to which the mp3 filename should be written to. This is useful for me because I rsync the whole tree between machines, as you will see there are options for writing an absolute pathname if you prefer. I should probably rewrite the script to do it relative to the playlist, but that’s another day.

#!/usr/bin/env python

# Trivial script to extract meta data from an mp3 file and add
# the mp3 file and data to an existing m3u file
# Colin Turner <>
# 2014
# GPL v2
# v 20140801.0 Initial Version
# v 20140802.0
# The mp3 filename is now, by default, written relative to the path of
# the playlist if possible.

import eyeD3
import re
import os

# We want to be able to process some command line options.
from optparse import OptionParser

def append(options, artist, title, seconds):
  'append the fetched data to the playlist'
  mp3_filename = resolve_mp3_filename(options)
  # Check if the playlist file exists
  there_is_no_spoon = not os.path.isfile(options.out_filename)

  with open(options.out_filename, 'a+') as playlist:
    # was the file frshly created?
    if there_is_no_spoon:
      # So write the header
      print >> playlist, "#EXTM3U"
      # There was a file, so check the last character, in case there was no \n, os.SEEK_END)
      last_char =
      if(last_char != '\n'):
        print >> playlist

    # OK, now able to write
    print >> playlist, "#EXTINF:%u,%s - %s" % (seconds, artist, title)
    print >> playlist, "%s" % mp3_filename

def resolve_mp3_filename(options):
  'resolve the mp3 filename appropriately, if we can, and if we are asked to'
  'there are three modes, depending on command line parameters:'
  '-l we write the filename precisely as on the command list'
  '-r specifies a base relative which to write the filename'
  'otherwise we try to resolve relative to the directory of the playlist'
  'the absolute filename will be the fall back position is resolution is impossible'

  if options.leave_filename:
    # we have been specifcally told not to resolve the filename
    mp3_filename = options.in_filename
    if options.verbose:
      print "Filename resolution disabled."

  if not options.leave_filename and not len(options.relative_to):
    # Neither argument used, automatcally resolve relative to the playlist
    (playlist_path, playlist_name) = os.path.split(os.path.abspath(options.out_filename))
    options.relative_to = playlist_path + os.path.sep
    if options.verbose:
      print "Automatic filename resolution relative to playlist base %s" % options.relative_to

  if len(options.relative_to):
    # We have been told to map the path relative to another path
    mp3_filename = os.path.abspath(options.in_filename)
    # Check that the root is actually present
    if mp3_filename.find(options.relative_to) == 0:
      # It is present and at the start of the line
      mp3_filename = mp3_filename.replace(options.relative_to, '', 1)

  if options.verbose:
    print "mp3 filename will be written as %s..." % mp3_filename
  return mp3_filename

def get_meta_data(options):
  'perform the append on the playlist'
  # read the existing data into three arrays in a tuple
  if options.verbose:
    print "Opening MP3 file %s ..." % options.in_filename
  if eyeD3.isMp3File(options.in_filename):
    # Ok, so it's an mp3
    audioFile = eyeD3.Mp3AudioFile(options.in_filename)
    tag = audioFile.getTag()
    artist = tag.getArtist()
    title = tag.getTitle()
    seconds = audioFile.getPlayTime()
    if not options.quiet:
      print "%s - %s (%s s)" % (artist, title, seconds)
    # OK, we have the required information, now time to write to the playlist
    return artist, title, seconds
    print "Not a valid mp3 file."

def print_banner():
  print "playlist-append"

def main():
  'the main function that kicks everything else off'

  usage = "usage: %prog [options] arg"
  parser = OptionParser(usage)
  parser.add_option("-m", "--mp3-file", dest="in_filename",
                    help="the FILENAME of the mp3 file to add")
  parser.add_option("-p", "--playlist-file", dest="out_filename",
                    help="the FILENAME of the playlist to append to")
  parser.add_option("-l", "--leave-filename", dest="leave_filename", action="store_false",
                    help="leaves the mp3 path as specified on the command line, rather than resolving it")
  parser.add_option("-r", "--relative-to", dest="relative_to", default="",
                    help="resolves mp3 filename relative to this path")
  parser.add_option("-v", "--verbose",
                    action="store_true", dest="verbose")
  parser.add_option("-q", "--quiet", default=False,
                    action="store_true", dest="quiet")

  (options, args) = parser.parse_args()
#  if len(args) == 0:
#      parser.error("use -h for more help")

  if not options.quiet:

  (artist, title, seconds) = get_meta_data(options)
  append(options, artist, title, seconds)

  if not options.quiet:
      print "Appended to playlist..."

if  __name__ == '__main__':


One thought on “Python script to add a file to a playlist”

Leave a Reply

Your email address will not be published. Required fields are marked *