Skip to content

CHill's Ramblings

Thoughts, ideas, and other such things I feel like putting here. About basically any topic.

Music Playlist Generator, Part 2

Posted on April 6, 2012August 2, 2021 By darkhelm No Comments on Music Playlist Generator, Part 2
0 0
Read Time:7 Minute, 12 Second

Part 1

Now that I had a XML file representing everything in my playlist, I needed to be able to actually use it within my Python script… so I could then, in turn, generate my daily playlist. I’m using PyPy, which is a rather unique variation of Python that can drastically speed up the performance of Python… so I figured why not? Anyway, this does pose some limitations on how I can proceed, as a number of Python XML parsing libraries are built in C with Python hooks, something PyPy doesn’t handle gracefully (yet). So I decided to stick with what I had in the Python Standard Library, which included the ever so convenient ElementTree that quickly and easily allows for me to process an XML file.

Of course, I needed to make a Python object to load the tracks into, so first off, I built my Track object:

class Track:
__slots__ = ('disc','discTotal','track','trackTotal','title','album','year','artist','albumArtist','genre','flags','filename','fileDate','length','size','channels','sampleRate','bitRate','fileType','fileTag','added','played')
def __init__(self):
self.disc = 0
self.discTotal = 0
self.track = 0
self.trackTotal = 0
self.title = ""
self.album = ""
self.year = ""
self.artist = ""
self.albumArtist = ""
self.genre = ""
self.flags = []
self.filename = ""
self.fileDate = ""
self.length = 0
self.size = 0
self.channels = ""
self.sampleRate = ""
self.bitRate = ""
self.fileType = ""
self.fileTag = ""
self.added = datetime.today()
self.played = []

def toTuple(self):
dat = (
self.disc, self.discTotal, self.track, self.trackTotal,
self.title, self.album, self.year, self.artist,
self.albumArtist, self.genre, self.flags, self.filename,
self.fileDate, self.length, self.size, self.channels,
self.sampleRate, self.bitRate, self.fileType,
self.fileTag
)

return dat

def __eq__(self, other):
if 'toTuple' not in dir(other):
return False
return self.toTuple().__eq__(other.toTuple())

def __ne__(self, other):
if 'toTuple' not in dir(other):
return False
return self.toTuple().__ne__(other.toTuple())

def __lt__(self, other):
if 'toTuple' not in dir(other):
return False
return self.toTuple().__lt__(other.toTuple())

def __le__(self, other):
if 'toTuple' not in dir(other):
return False
return self.toTuple().__le__(other.toTuple())
def __gt__(self, other):
if 'toTuple' not in dir(other):
return False
return self.toTuple().__gt__(other.toTuple())

def __ge__(self, other):
if 'toTuple' not in dir(other):
return False
return self.toTuple().__ge__(other.toTuple())

def checkSum(self):
return hashlib.sha512(str(self.toTuple())).hexdigest()

def copy(self, other):
log.info("Changing "%s":", self.filename)

logLine = "t%s (%s -> %s)"
logLineQ = "t%s ("%s" -> "%s")"

if self.disc != other.disc:
log.info(logLine, "Disc", str(self.disc), str(other.disc))
self.disc = other.disc

if self.discTotal != other.discTotal:
log.info(logLine, "Disc Total", str(self.discTotal), str(other.discTotal))
self.discTotal = other.discTotal

if self.track != other.track:
log.info(logLine, "Track", str(self.track), str(other.track))
self.track = other.track

if self.trackTotal != other.trackTotal:
log.info(logLine, "Track Total", str(self.trackTotal), str(other.trackTotal))
self.trackTotal = other.trackTotal

if self.title != other.title:
log.info(logLineQ, "Title", str(self.title), str(other.title))
self.title = other.title

if self.album != other.album:
log.info(logLineQ, "Album", str(self.album), str(other.album))
self.album = other.album

if self.year != other.year:
log.info(logLine, "Year", str(self.year), str(other.year))
self.year = other.year

if self.artist != other.artist:
log.info(logLineQ, "Artist", str(self.artist), str(other.artist))
self.artist = other.artist

if self.albumArtist != other.albumArtist:
log.info(logLineQ, "Album Artist", str(self.albumArtist), str(other.albumArtist))
self.albumArtist = other.albumArtist

if self.genre != other.genre:
log.info(logLineQ, "Genre", str(self.genre), str(other.genre))
self.genre = other.genre

if self.flags != other.flags:
log.info(logLineQ, "Flags", str(self.flags), str(other.flags))
self.flags = other.flags

if self.filename != other.filename:
log.info(logLineQ, "Filename", str(self.filename), str(other.filename))
self.filename = other.filename

if self.fileDate != other.fileDate:
log.info(logLine, "File Date", str(self.fileDate), str(other.fileDate))
self.fileDate = other.fileDate

if self.length != other.length:
log.info(logLine, "Length", str(timedelta(0, self.length)), str(timedelta(0, other.length)))
self.length = other.length

if self.size != other.size:
log.info(logLine, "Size", str(self.size), str(other.size))
self.size = other.size

if self.channels != other.channels:
log.info(logLineQ, "Channels", str(self.channels), str(other.channels))
self.channels = other.channels

if self.sampleRate != other.sampleRate:
log.info(logLineQ, "Sample Rate", str(self.sampleRate), str(other.sampleRate))
self.sampleRate = other.sampleRate

if self.bitRate != other.bitRate:
log.info(logLineQ, "Bit Rate", str(self.bitRate), str(other.bitRate))
self.bitRate = other.bitRate

if self.fileType != other.fileType:
log.info(logLineQ, "File Type", str(self.fileType), str(other.fileType))
self.fileType = other.fileType

if self.fileTag != other.fileTag:
log.info(logLineQ, "File Tag", str(self.fileTag), str(other.fileTag))
self.fileTag = other.fileTag

This basically is an object that just holds onto the various XML sub-elements for the XML track into a single object for me to work with. It has a couple of other pieces, namely it some some overriding for comparison operators, has a way to copy a track from another track, and has a way to produce a tuple representation of the object that I use with the difflib for figuring out when two tracks are most likely the same (explained later).

Then, there is the actual import function:

def importData():
"""
Imports a new set of data from the XML file.
"""
listIter = ET.iterparse(XML_FILENAME)

tracks = {}
track = Track()
trackCat = None

for listItem in listIter:
trackElem = listItem[1]
if trackElem.tag == "track":
if trackCat not in tracks:
tracks[trackCat] = []
tracks[trackCat].append(track)
track = Track()
elif trackElem.tag == "discnumber":
track.disc = int(trackElem.text) if trackElem.text != None else None

elif trackElem.tag == "totaldiscs":
track.discTotal = int(trackElem.text) if trackElem.text != None else None

elif trackElem.tag == "tracknumber":
track.track = int(trackElem.text) if trackElem.text != None else None

elif trackElem.tag == "totaltracks":
track.trackTotal = int(trackElem.text) if trackElem.text != None else None

elif trackElem.tag == "title":
if trackElem.text == None:
track.title = u''
else:
track.title = trackElem.text

elif trackElem.tag == "album":
if trackElem.text == None:
track.album = u''
else:
track.album = trackElem.text

elif trackElem.tag == "year":
if trackElem.text == None:
track.year = u''
else:
track.year = trackElem.text

elif trackElem.tag == "artist":
if trackElem.text == None:
track.artist = u''
else:
track.artist = trackElem.text

elif trackElem.tag == "albumartist":
if trackElem.text == None:
track.albumArtist = u''
else:
track.albumArtist = trackElem.text

elif trackElem.tag == "genre":
if trackElem.text == None:
track.genre = u''
else:
track.genre = trackElem.text

elif trackElem.tag == "category":
if trackElem.text == None:
tracvCat = u''
else:
trackCat = trackElem.text

elif trackElem.tag == "flags":
if trackElem.text == None:
track.flags = []
else:
track.flags = trackElem.text.split(",")

elif trackElem.tag == "filename":
if trackElem.text == None:
track.filename = u''
else:
track.filename = trackElem.text

elif trackElem.tag == "filedate":
track.fileDate = datetime.strptime(trackElem.text, '%m/%d/%Y %I:%M:%S %p') if trackElem.text != None else None

elif trackElem.tag == "length":
track.length = int(trackElem.text) if trackElem.text != None else None

elif trackElem.tag == "size":
track.size = int(trackElem.text) if trackElem.text != None else None

elif trackElem.tag == "channels":
if trackElem.text == None:
track.channels = u''
else:
track.channels = trackElem.text

elif trackElem.tag == "samplerate":
track.sampleRate = int(trackElem.text) if trackElem.text != None else None

elif trackElem.tag == "bitrate":
track.bitRate = int(trackElem.text) if trackElem.text != None else None

elif trackElem.tag == "type":
if trackElem.text == None:
track.fileType = u''
else:
track.fileType = trackElem.text

elif trackElem.tag == "tag":
if trackElem.text == None:
track.fileTag = u''
else:
track.fileTag = trackElem.text

for trackList in tracks.values():
random.shuffle(trackList)

for track in trackList:
track.added = datetime.today()

return tracks

This does two things, it rapidly loads every track from the XML file, and then… because I have had to rebuild my datafile a number of times and don’t want to keep playing the same set of tracks (through the LRA process explained later), I shuffle the tracks and assign the added date in random order.

Once this is completed, I am able to use the tracks in Python using the tracks object I built. I partition my music list along “category” lines, which I use the “categorygroup” or other equivalent tags within my music to identify what goes to which category. Everything about my playlist works from this set of categories — no song exists in two categories, so it really does a complete partitioning of my music, which means I can process each category completely independent of each other… something very useful later in the code.

Share

Facebook
Twitter
Pinterest
LinkedIn

About Post Author

darkhelm

chill@darkhelm.org
http://darkhelm.org
Happy
Happy
0 0 %
Sad
Sad
0 0 %
Excited
Excited
0 0 %
Sleepy
Sleepy
0 0 %
Angry
Angry
0 0 %
Surprise
Surprise
0 0 %
Uncategorized

Post navigation

Previous Post: Music Playlist Generator… or how I like to make things complicated…
Next Post: All Hallow’s Eve

Average Rating

5 Star
0%
4 Star
0%
3 Star
0%
2 Star
0%
1 Star
0%
(Add your review)

Leave a Reply Cancel reply

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

Archives

  • October 2021
  • August 2021
  • October 2019
  • November 2018
  • October 2016
  • September 2016
  • November 2015
  • September 2013
  • May 2013
  • October 2012
  • April 2012
  • March 2012
  • December 2010
  • November 2010
  • September 2010
  • August 2010
  • July 2010
  • January 2010

Categories

  • america
  • bitsy
  • blueprints
  • ejb
  • glassfish
  • gwt-syntaxhighlighter
  • jpa
  • jython
  • lies
  • network
  • politics
  • Uncategorized

Recent Posts

  • To Dave Hines, my friend, may you now have the joy you had spread to everyone around you.
  • Router Fun
  • False Peace
  • Moving away from the google universe.
  • The problem with people abusing scripture to attack entertainment

Recent Comments

  1. darkhelm on To Dave Hines, my friend, may you now have the joy you had spread to everyone around you.
  2. Matt Sutton on To Dave Hines, my friend, may you now have the joy you had spread to everyone around you.
  3. Unknown on Jonah, Jonah, did not obey God immediately…
  4. 1seanv on A Christian’s Response To: A Christian’s View of World of Warcraft (published in 2008)
  5. Unknown on Jonah, Jonah, did not obey God immediately…

Copyright © 2023 CHill's Ramblings.

Powered by PressBook WordPress theme