You are not logged in.
I present to you, fellow Archers, two little programs. One I've had for months but haven't shared for some reason, and the other I finished a short while ago.
MPDDP
MPDDP: MPD Dynamic Playlists is a program to generate, as the name would imply, a dynamic playlist for MPD. You specify rules about what tracks you want added, and it keeps adding them randomly.
Source:
#!/usr/bin/env python
# MPDDP: MPD Dynamic Playlists
# Call this and run it in the background (eg mpddp &>/dev/null &)
# Configured in /etc/mpddp.conf, See /etc/mpddp.conf.example.
import mpd, random, os, time, sys, string
client = mpd.MPDClient()
host = "" # The host MPD is operating upon
port = 0 # The port MPD is operating upon
playlistlen = 0 # The len of the playlist
changeafter = 0 # The number of tracks before more are added/removed to/from the playlist
clearinitially = '' # Whether to clear the playlist initially or not.
saveonquit = '' # Whether to save/load the playlist on exit/start or not.
update = '' # Whether to periodically check the config file / filesystem for changes.
confdir = '/etc/mpddp.conf' # The path of the main MPDDP config file.
savedir = '' # The folder where MPDDP saves and loads files.
alltracks = [] # All the tracks that can be played.
oldconfig = [] # The configuration as it was last loaded.
def pickNewTrack(): # Pick and remove a track from the list, append it to the current list, and return the name.
global client
global alltracks
index = random.randint(0, len(alltracks) - 1)
track = alltracks[index]
return track
def addNewTrackToPlaylist(): # Pick a new track, update the lists, and add it to the playlist.
global client
global host
global port
global playlistlen
client.connect(host, port)
playlist = client.playlistinfo()
client.disconnect()
if len(playlist) < playlistlen:
track = pickNewTrack()
print "Adding", track
client.connect(host, port)
client.add(track)
client.disconnect()
def removeLastTrackFromPlaylist(): # Delete the oldest track from the playlist.
global client
global host
global port
client.connect(host, port)
playlist = client.playlistinfo()
client.delete(0)
client.disconnect()
print "Removing", playlist[0]['file']
def checkMPDPlaylist(): # Add enough tracks to the MPD playlist to repopulate it if it is almost empty.
global client
global host
global port
global playlistlen
global alltracks
client.connect(host, port)
playlist = client.playlistinfo()
client.disconnect()
if len(playlist) < playlistlen:
while len(playlist) < playlistlen:
addNewTrackToPlaylist()
def updatePlaylist(): # Update the MPD playlist, and the internal representation of it if necessary.
checkMPDPlaylist()
removeLastTrackFromPlaylist()
addNewTrackToPlaylist()
def getFilenamesFromMPDSPL(expression):
os.system('mpdspl -s -n misc "' + expression + '" >/tmp/mpddp-mpdspl-temp.txt')
a = open('/tmp/mpddp-mpdspl-temp.txt')
ot = a.read()
ot = ot.splitlines()
a.close()
os.remove('/tmp/mpddp-mpdspl-temp.txt')
return ot
def getFilenamesFromMPD(rules): # Gets the filenames from MPD of all files which match the specified rules.
global client
global host
global port
paths = []
playlists = []
smarts = []
nevers = []
tracks = []
for rule in rules:
if rule[0] == 'path' and not rule[1] in paths:
paths.append(rule[1])
elif rule[0] == 'playlist' and not rule[1] in playlists:
playlists.append(rule[1])
elif rule[0] == 'smart' and not rule[1] in smarts:
smarts.append(rule[1])
elif rule[0] == 'never' and not rule[1] in nevers:
nevers.append(rule[1])
client.connect(host, port)
for path in paths:
temptracks = client.search("file", path)
for track in temptracks:
if isinstance(track, dict):
track = track['file']
dontadd = False
for never in nevers:
if never in track:
dontadd = True
if dontadd == False and not track in tracks:
tracks.append(track)
for playlist in playlists:
temptracks = client.listplaylist(playlist)
for track in temptracks:
if isinstance(track, dict):
track = track['file']
dontadd = False
for never in nevers:
if never in track:
dontadd = True
if dontadd == False and not track in tracks:
tracks.append(track)
for smart in smarts:
temptracks = getFilenamesFromMPDSPL(smart)
for track in temptracks:
dontadd = False
for never in nevers:
if never in track:
dontadd = True
if dontadd == False and not track in tracks:
tracks.append(track)
client.disconnect()
return tracks
def parseConfigIncludes(conf, path): # Parse a config file
outconf = ""
paths = path
for line in conf:
line = line.split("#")
line = line[0]
line = line.strip()
if len(line) > 0:
if line[0:7] == 'include':
toinclude = line[8:].strip()
toinclude = toinclude.replace("~", os.path.expanduser("~"))
if not toinclude in paths:
paths.append(toinclude)
filehandler = open(toinclude)
newconf = parseConfigIncludes(filehandler, paths)
outconf = outconf + newconf
filehandler.close()
else:
outconf = outconf + line + "\r\n"
return outconf
def parseConfigLine(line): # Parse a line from the configuration file and return what it means.
line = line.split("#")
line = line[0]
line = line.strip()
if len(line) > 0:
if ("=" in line) and (not ":" in line):
pline = line.split("=", 1)
parsed = {'type' : pline[0].strip(),
'value' : pline[1].strip()}
return parsed
elif ":" in line:
pline = line.split(":", 1)
parsed = {'type' : 'rule',
'value' : [pline[0].strip(), pline[1].strip()]}
return parsed
else:
return {'type' : 'unrecognised'}
else:
return {'type' : 'blankline'}
def parseConfigFile(): # Open the configuration file and parse the rules.
global confdir
filehandler = open(confdir)
conf = parseConfigIncludes(filehandler, [confdir])
output = {'rules' : [],
'server' : 'localhost',
'port' : 6600,
'playlistlen' : 15,
'changeafter' : 8,
'clearinitially' : 'yes',
'saveonquit' : 'no',
'savedir' : '/var/lib/mpddp/',
'update' : 'no'}
for line in conf.splitlines():
result = parseConfigLine(line)
if result['type'] == 'rule':
output['rules'].append(result['value'])
elif result['type'] == 'clearinitially' or result['type'] == 'saveonquit':
if result['value'] == 'yes' or result['value'] == 'no':
output[result['type']] = result['value']
else:
print "Invalid value specified for", result['type']
elif result['type'] == 'port' or result['type'] == 'playlistlen' or result['type'] == 'changeafter':
try:
if (result['type'] == 'port' and int(result['value']) > 0 and int(result['value']) <= 65536) or not result['type'] == 'port':
output[result['type']] = int(result['value'])
else:
print "Invalid value specified for", result['type']
except TypeError:
print "Invalid value specified for", result['type']
elif result['type'] == 'server' or result['type'] == 'savedir' or result['type'] == 'update':
output[result['type']] = result['value']
filehandler.close()
return output
def loadPlaylistFromSaved(): # Load the previously saved playlist, if it exists. Then fill any space remaining with newly-added tracks.
global playlistlen
global client
global host
global port
global savedir
loaded = []
try:
filehandler = open(savedir + 'playlist')
for line in filehandler:
loaded.append(line)
filehandler.close()
client.connect(host, port)
for track in loaded:
track = track.strip()
if len(track) > 0:
try:
client.add(track)
print "Loading", track
except mpd.CommandError:
print "Error loading", track
client.disconnect()
for i in range(len(loaded), playlistlen):
addNewTrackToPlaylist()
except IOError:
for i in range(0, playlistlen):
addNewTrackToPlaylist()
def populateLists(redoing): # Parse the configuration file, and grab the tracks from MPD to populate the lists.
global playlistlen
global changeafter
global clearinitially
global client
global host
global port
global alltracks
global saveonquit
global savedir
global update
global oldconfig
config = parseConfigFile()
rules = config['rules']
if redoing == False or (redoing == True and not oldconfig == config):
host = config['server']
port = config['port']
playlistlen = config['playlistlen']
changeafter = config['changeafter']
clearinitially = config['clearinitially']
saveonquit = config['saveonquit']
savedir = config['savedir']
update = config['update']
oldconfig = config
print "Configuration updated:", config
tracks = getFilenamesFromMPD(rules)
if redoing == True:
alltracks = []
if redoing == False or not alltracks == tracks:
alltracks = tracks
client.connect(host, port)
if clearinitially == 'yes' and redoing == False:
client.clear()
client.random(0)
client.disconnect()
if redoing == False:
if saveonquit == 'no':
for i in range(0, playlistlen):
addNewTrackToPlaylist()
else:
loadPlaylistFromSaved()
client.connect(host, port)
client.play()
client.disconnect()
def dieGracefully():
global saveonquit
if saveonquit == 'yes':
try:
os.remove('/var/lib/mpddp/playlist')
print "Removed old playlist..."
except OSError:
print "No old playlist to remove."
try:
os.remove('/tmp/killmpddp')
print "Removed kill file..."
except OSError:
print "No kill file to remove."
print "Saving playlist to", savedir, "playlist"
filehandler = open(savedir + 'playlist', 'w')
playlist = client.playlistinfo()
for track in playlist:
print "Writing", track['file']
filehandler.write(track['file'] + '\n')
filehandler.close()
print "Quitting..."
sys.exit()
# Execute the program main loop
populateLists(False)
loops = 0
try:
while True:
if os.path.exists('/tmp/killmpddp'):
dieGracefully()
client.connect(host, port)
info = client.currentsong()
status = client.status()
playlist = client.playlistinfo()
client.disconnect()
if len(info) > 0:
if int(status['song']) >= changeafter:
for i in range(changeafter - 1, int(status['song'])):
updatePlaylist()
if len(playlist) < playlistlen:
for i in range(len(playlist), playlistlen):
addNewTrackToPlaylist()
if loops == 59:
if update == 'yes':
populateLists(True)
loops = 0
else:
loops = loops + 1
time.sleep(1)
except KeyboardInterrupt:
dieGracefully()
/etc/mpddp.conf.example:
server = localhost # The server that MPD is operating upon.
port = 6600 # The port that MPD is operating upon.
playlistlen = 15 # The number of tracks to have in the playlist.
changeafter = 8 # The number of tracks listened to initially before the add/remove loop begins.
clearinitially = no # Whether to clear the playlist upon starting or not.
saveonquit = no # Whether to save/load the playlist upon exit/start or not.
savedir = /var/lib/mpddp/ # The directory to save/load the playlist.
update = no # Periodically check to ensure that the config file hasn't been updated, and that no tracks have been added/removed.
#include /home/USER/.mpddp # An additional config file to parse. I suggest you use this to specify tracks to listen to
#path:PATH # Add all tracks where the file path contains PATH.
#playlist:PLAYLIST # Add all the playlists where the name is PLAYLIST.
#smart:RULES # Add all the tracks which match the smart playlist RULES. Requires MPDSPL to be in your $PATH.
#never:STRING # Never add tracks where the file path contains STRING.
And, for reference, here's a small chunk of my config file:
# -*-conf-*-
playlistlen = 50 # For full-screen
changeafter = 26
#playlistlen = 19 # For half-screen
#changeafter = 10
clearinitially = no
# Playlists
#playlist:Wordless
#playlist:Steamcowboy
# Musicals
#smart:fp=/(Cats Original London Cast|Joseph and the Amazing Technicolour Dreamcoat|Les Misérables .*|Mary Poppins|Miss Saigon .*|Phantom Of The Opera .*|Sound of Music 40th Anniversary Special Edition, The).*\//
#path:Cats Original London Cast/
#path:Joseph and the Amazing Technicolour Dreamcoat/
#path:Les Misérables Complete Symphonic Recording/
#path:Les Misérables Original Broadway Cast/
#path:Les Misérables Original Paris Cast/
#path:Mary Poppins/
#path:Miss Saigon CSR/
#path:Miss Saigon (Original London Cast)/
#path:Phantom Of The Opera 2004 Film, The/
#path:Phantom of the Opera Original London Cast, The/
#path:Sound of Music 40th Anniversary Special Edition, The/
MPDSPL
MPDSPL: MPD Smart PlayLists makes smart playlists for MPD. See the "smart:" line in my config file above? that's an example of a MPDSPL playlist description. Playlist descriptions are in regex, and use keywords ("ar" for artist, "al" for album, etc).
Source:
#!/usr/bin/env python
# A script to parse the MPD database into a list of dictionaries (or at least, it was going to be before I decided to finish it).
# Now with patronising comments which assume almost no Python knowledge!
# cPickle is a faster version of the pickle library. It is used to save data structures to a file. Like lists and dictionaries. os is needed for file stuff, sys for arguments, and re for regex.
import cPickle, os, sys, re
# Info about new playlists
newname = ""
newrules = []
# Place to look for the MPD database and config files, and the loaded MPD config (well, only the values useful to us).
confpath = "/etc/mpd.conf"
mpd = {"music_directory" : "", "playlist_directory" : "", "db_file" : "", "user" : ""}
# There is an environmental variable XDG_CACHE_HOME which specifies where to save cache files. However, if not set, a default of ~/.cache should be used.
cachehome = os.path.expanduser(os.environ['XDG_CACHE_HOME'])
if cachehome == "":
cachehome = os.environ['HOME'] + "/.cache"
cachepath = cachehome + "/mpdspl/mpddb.cache"
# $XDG_DATA_HOME specifies where to save data files. Like a record of playlists which have been created. If unset a default of ~/.local/share should be used. This is currently unused as there is no actual creation of playlists yet :p
datahome = os.path.expanduser(os.environ['XDG_DATA_HOME'])
if datahome == "":
datahome = os.environ['HOME'] + "/.local/share/"
datapath = datahome + "/mpdspl"
# If the data directory does not exist, create it.
if not os.path.isdir(datapath):
os.mkdir(datapath)
tracks = []
forceupdate = False
simpleoutput = False
# A nice little help function. Read on to see how it is called...
def showhelp():
print "Usage: mpdspl [options]\n"
print "A script to generate smart playlists for MPD. Currently does nothing of use :p\n"
print "Options:"
print " -f, --force - Force an update of the cache file and any playlists."
print " -dFILE, --dbpath=FILE - Location of the database file."
print " -cFILE, --cachepath=FILE - Location of the cache file."
print " -CFILE, --confpath=FILE - Location of the MPD config file."
print " -uUSER, --mpduser=USER - Location of the MPD config file."
print " -n, --new [name] [rules] - Create a new playlist."
print " -s, --simple - (used with -n) Only print the final track list (with paths relative to the MPD root dir) to STDOUT."
print " -h, --help - Display this text and exit.\n"
print "Playlist rules:"
print " These are specified as a string of Python-compatible regular expressions separated by keywords, spaces, and slashes."
print " They are matched by re.search, not re.match, and no special flags are passed, other than re.IGNORECASE when requested.\n"
print " These keywords are:"
print " ar = Artist"
print " al = Album"
print " ti = Title"
print " tr = Track Number"
print " ge = Genre"
print " ye = Year"
print " le = Length (seconds)"
print " fp = File Path (relative to MPD root dir, including filename)"
print " fn = File Name\n"
print " Regular expressions are specified within slashes (/regex/)."
print " If the first slash is preceeded by an 'i', the regular expression is interpreted as case-insensitive."
print " If the final slash is succeeded by a 'n', the result of the match is negated.\n"
print " For example, a rule for all tracks by 'Fred' or 'George', which have a title containing (case insensitive) 'The' and 'and', but not 'when' would be:"
print " ar=/(Fred|George)/ ti=i/(the.*and|and.*the)/ ti=i/when/n\n"
print "Notes:"
print " Paths specified in the MPD config file containing a '~' will have the '~'s replaced by the user MPD runs as."
print " If the user is not specified in the MPD config file, or by the -u parameter, it is assumed the user is root."
print " Backslashes must be escaped in playlist rules.\n"
sys.exit()
# Parse the rules regex
def parserules(rulestr):
# rules will be our list of rules, bufferstr will be the buffer for our parser, and i will be a counter
rules = []
bufferstr = ""
i = 0
# We want to use the same identifiers as the track dictionaries:
keywords = {"ar" : "Artist", "al" : "Album", "ti" : "Title", "tr" : "Track", "ge" : "Genre", "ye" : "Date", "le" : "Time", "fp" : "file", "fn" : "key"}
# For every character in rulestr (we do it characterwise, hence needing a buffer)
for c in rulestr:
# Add the character to the buffer
bufferstr += c
# If the buffer matches one of our keywords, we have hit a new rule, and so create a blank dictionary, and clear the buffer.
if bufferstr.strip() in ["ar", "al", "ti", "tr", "ge", "ye", "le", "fp", "fn"]:
rules.append({"type" : keywords[bufferstr.strip()], "regex" : "", "compiled" : None, "inverse" : False, "negate" : False})
bufferstr = ""
# If we're at the start of a blank case-insensitive regex, record that, and clear the buffer.
elif bufferstr == "=i/":
rules[i]["i"] = True
bufferstr = ""
# If not, just clear the buffer for the coming regex.
elif bufferstr == "=/":
bufferstr = ""
# If at the end of a regex, stick it all (sans the trailing slash, they're just a nice separater for our parser) to the dictionary, increment the counter, and clear the buffer ready for the next rule.
elif bufferstr[-1] == "/" and not bufferstr[-2] == "\\":
rules[i]["regex"] = bufferstr[:-1]
bufferstr = ""
i += 1
# Get rid of the escape backslash if a forward slash has been used.
elif bufferstr[-1] == "/" and not bufferstr[-2] == "\\":
bufferstr[-2] = ""
# If set to 'n' and the regex has been set, negate it.
elif bufferstr == "n" and not rules[i - 1]["regex"] == "":
bufferstr = ""
rules[i - 1]["negate"] = True
# This isn't needed. But it makes things faster and allows us to have case insensetivity.
for rule in rules:
regex = None
if rule["inverse"]:
# If case insensitive, compile it as such.
regex = re.compile(rule["regex"], re.IGNORECASE)
else:
regex = re.compile(rule["regex"])
# Overwrite the regex string with the compiled object
rule["compiled"] = regex
return rules
# Splitting things up into functions is good :D
def parseargs():
# global lets us access variables specified outside our function.
global forceupdate
global mpd
global confpath
global cachepath
global newname
global newrules
global simpleoutput
newarg = 0
for argument in sys.argv:
if not newarg == 0:
# We're making a new playlist. If we're only on the first option after -n, that's the name. If the second, that's the description.
if newarg == 2:
newname = argument
elif newarg == 1:
newrules = parserules(argument)
newarg -= 1
else:
if argument == "-f" or argument == "--force":
# If a "-f" or "--force" parameter is sent, force the cache to be updated even if it doesn't look like it needs to be.
forceupdate = True
elif argument[:2] == "-d" or argument[:9] == "--dbpath=":
# Looks like their db is somewhere other than /var/lib/mpd/mpd.db...
if argument[:2] == "-d":
# Python can't work with ~, which has a reasonable chance of being used (eg: ~/.mpd/mpd.db"), so it needs to be expanded.
mpd["db_file"] = os.path.expanduser(argument[2:])
elif argument[:9] == "--dbpath=":
mpd["db_file"] = os.path.expanduser(argument[9:])
elif argument[:2] == "-c" or argument[:12] == "--cachepath=":
# Silly person, not keeping their cache where XDG says it should be...
if argument[:2] == "-c":
cachepath = os.path.expanduser(argument[2:])
elif argument[:12] == "--cachepath=":
cachepath = os.path.expanduser(argument[12:])
elif argument[:2] == "-C" or argument[:11] == "--confpath=":
# Now any person which this code applies to is just awkward.
if argument[:2] == "-C":
confpath = os.path.expanduser(argument[2:])
elif argument[:11] == "--confpath=":
confpath = os.path.expanduser(argument[11:])
elif argument[:2] == "-u" or argument[:10] == "--mpduser=":
# As is any person to whom this applies...
if argument[:2] == "-u":
mpd["user"] = argument[2:]
elif argument[:10] == "--mpdpath=":
mpd["user"] = argument[10:]
elif argument == "-n" or argument == "--new":
# Do special treatment to the next 2 arguments
newarg = 2
elif argument == "-s" or argument == "--simple":
# Ooh, this means that (probably) MPDDP is being used! Yay!
simpleoutput = True
elif argument == "-h" or argument == "--help":
showhelp()
elif not argument == sys.argv[0]: # The first argument is the filename. Don't complain about not understanding it...
# Ooh, stderr. I never actually knew how to send stuff through stderr in python.
print >> sys.stderr, "Unrecognised parameter '" + argument + "'"
sys.exit(1)
# A function to parse a MPD database and make a huge list of tracks
def parsedatabase(database):
global tracks
i = -1
parsing = False
for line in database:
# For every line in the database, remove any whitespace at the beginning and end so the script isn't buggered.
line = line.strip()
# If entering a songList, start parsing. If exiting one, stop. Fairly self explanatory.
if not parsing and line == "songList begin":
parsing = True
elif parsing and line == "songList end":
parsing = False
# If we get a line to parse which is not a "songList begin" statement (because it's be stupid to do things with that)
if parsing and not line == "songList begin":
if line[0:5] == "key: ":
i += 1
# Increment the counter and make an empty dictionary if we hit the beginning of a track
tracks.append({"key" : "", "file" : "", "Time" : "", "Genre" : "", "Title" : "", "Artist" : "", "Date" : "", "Album" : "", "Track" : "", "mtime" : ""})
# Split the line by the first ": ", the string MPD uses, and stick the second part (the value) in the bit of the dictionary referred to by the first part (the key)
splitted = line.split(": ", 1)
tracks[i][splitted[0]] = splitted[1]
# Grabbing stuff from the MPD config, a very important step
def parsempdconf():
global confpath
global mpd
config = open(confpath, "r")
# Don't load the user or db_file values if they've already been told to us
holduser = not mpd["user"] == ""
holddb = not mpd["db_file"] == ""
for line in config:
line = line.strip()
if line[:15] == "music_directory":
rest = line[15:].strip()
mpd["music_directory"] = rest[1:-1]
elif line[:18] == "playlist_directory":
rest = line[18:].strip()
mpd["playlist_directory"] = rest[1:-1]
elif line[:7] == "db_file" and not holddb:
rest = line[7:].strip()
mpd["db_file"] = rest[1:-1]
# The rest of the code in this function wouldn't be needed if I could assume nobody would use "~" in their MPD config...
elif line[:4] == "user" and not holduser:
rest = line[4:].strip()
mpd["user"] = rest[1:-1]
if mpd["user"] == "":
mpd["user"] = "root"
homedir = "/home/" + mpd["user"]
if homedir == "/home/root":
homedir = "/root"
if "~" in mpd["music_directory"]:
mpd["music_directory"] = mpd["music_directory"].replace("~", homedir)
if "~" in mpd["playlist_directory"]:
mpd["playlist_directory"] = mpd["playlist_directory"].replace("~", homedir)
if "~" in mpd["db_file"]:
mpd["db_file"] = mpd["db_file"].replace("~", homedir)
def findtracks():
global tracks
global newrules
# matchingtracks will hold all tracks which match all of the criteria.
matchingtracks = []
for track in tracks:
# Initially assume a track *will* be added.
addtrack = True
for rule in newrules:
# For every track, check it with every rule
if rule["negate"]:
if not re.search(rule["compiled"], track[rule["type"]]) == None:
# If the regular expression matches the track, do not add it to the matchingtracks list.
addtrack = False
else:
if re.search(rule["compiled"], track[rule["type"]]) == None:
# If the regular expression does not match the track, do not add it to the matchingtracks list.
addtrack = False
if addtrack:
# Add the track if appropriate
matchingtracks.append(track)
return matchingtracks
def genplaylist(tracks):
global mpd
# Parse a list of track dictionaries into a playlist. Thankfully, m3u is a *very* simple format.
playlist = ""
for track in tracks:
playlist += mpd["music_directory"] + "/" + track["file"] + "\n"
return playlist
# Save some random gubbage to a file
def savegubbage(data, path):
if not os.path.isdir(os.path.dirname(path)):
os.mkdir(os.path.dirname(path))
# Open the file for writing in binary mode
outfile = open(path, "wb")
# Send the stuff to the file with the magic of cPickle
cPickle.dump(data, outfile)
# Close the file handler. Tidy u[p.
outfile.close()
# We might be running as someone other than the user, so make the file writable
os.chmod(path, 438)
def loadgubbage(path):
infile = open(path, "rb")
data = cPickle.load(infile)
infile.close()
return data
def saveplaylist():
global newname
global newrules
global mpd
global datapath
global simpleoutput
matchingtracks = findtracks()
playlist = genplaylist(matchingtracks)
if simpleoutput:
for track in matchingtracks:
print track["file"]
else:
print "Saving playlist '" + newname + "'."
# Write the contents of the playlist to the m3u file
newlist = open(mpd["playlist_directory"] + "/" + newname + ".m3u", "w")
newlist.write(playlist)
newlist.close()
# Save as list object. This lets us load them all into a big list nicely.
savegubbage([newname, newrules], datapath + "/" + newname)
# Parse some options!
parseargs()
parsempdconf()
# Check that the database is actually there before attempting to do stuff with it.
if not os.path.exists(mpd["db_file"]):
print >> sys.stderr, "The database file '" + mpd["db_file"] + "' could not be found."
sys.exit(1)
# If the cache file does not exist OR the database has been modified since the cache file has this has the side-effect of being able to touch the cache file to stop it from being updated. Good thing we have the -f option for any accidental touches (or if you copy the cache to a new location).
if not os.path.exists(cachepath) or os.path.getmtime(mpd["db_file"]) > os.path.getmtime(cachepath) or forceupdate:
if not simpleoutput:
print "Updating database cache..."
# If the cache directory does not exist, create it. The dirname function just removes the "/mpddb.cache" from the end.
if not os.path.isdir(os.path.dirname(cachepath)):
os.mkdir(os.path.dirname(cachepath))
database = open(mpd["db_file"], "r")
# Now, parse that database!
parsedatabase(database)
# Save the parsed stuff to the cache file and close the database file handler. That's not strictly required, python will clean up when the script ends, but you can't unmount volumes with file handlers pointing to them, so it makes a mess.
savegubbage(tracks, cachepath)
database.close()
if not simpleoutput:
# Let's update those playlists!
playlistfiles = os.listdir(datapath)
playlists = []
for playlistfile in playlistfiles:
playlists.append(loadgubbage(datapath + "/" + playlistfile))
# Backup the values first.
oldnewname = newname
oldnewrules = newrules
# Now regenerate!
for playlist in playlists:
newname = playlist[0]
newrules = playlist[1]
saveplaylist()
# And restore.
newname = oldnewname
newrules = oldnewrules
else:
# Oh, goodie, we don't need to go through all that arduous parsing as we have a valid cache file :D
if not simpleoutput:
print "Loading database cache..."
# Open it for reading, load the stuff in the file into the tracks list, close the file handler, and have a party.
tracks = loadgubbage(cachepath)
# See if we're making a new playlist or not
if not newname == "":
# We are, go go go!
saveplaylist()
The source is full of simple comments because I was supposed to be helping a friend who's less proficient in Python make it but… I got bored of waiting
If your MPD playlists directory is somewhere which you don't have write access to, run it with `sudo -E`
If you need any help with either, ask and ye shall receive. If what you need help with is covered in this post, mpdspl -h, or /etc/mpddp.conf.example, ask and ye shall be laughed at
Last edited by Barrucadu (2009-07-20 17:08:32)
Offline