#!/usr/bin/python import xml.etree.ElementTree as ET class XspfBase(object): NS = "http://xspf.org/ns/0/" def _addAttributesToXml(self, parent, attrs): for attr in attrs: value = getattr(self, attr) if value: el = ET.SubElement(parent, "{{{0}}}{1}".format(self.NS, attr)) el.text = value def _addDictionaryElements(self, parent, name, values): # Sort keys so we have a stable order of items for testing. # Alternative would be SortedDict, but is >=2.7 for k in sorted(values.keys()): el = ET.SubElement(parent, "{{{0}}}{1}".format(self.NS, name)) el.set("rel", k) el.text = values[k] # Avoid namespace prefixes, VLC doesn't like it if hasattr(ET, 'register_namespace'): ET.register_namespace('', XspfBase.NS) # in-place prettyprint formatter # From http://effbot.org/zone/element-lib.htm def indent(elem, level=0): i = "\n" + level*" " if len(elem): if not elem.text or not elem.text.strip(): elem.text = i + " " if not elem.tail or not elem.tail.strip(): elem.tail = i for elem in elem: indent(elem, level+1) if not elem.tail or not elem.tail.strip(): elem.tail = i else: if level and (not elem.tail or not elem.tail.strip()): elem.tail = i class Xspf(XspfBase): def __init__(self, obj={}, **kwargs): self.version = "1" self._title = "" self._creator = "" self._info = "" self._annotation = "" self._location = "" self._identifier = "" self._image = "" self._date = "" self._license = "" self._attributions = [] self._link = {} self._meta = {} self._trackList = [] if len(obj): if "playlist" in obj: obj = obj["playlist"] for k, v in list(obj.items()): setattr(self, k, v) if len(kwargs): for k, v in list(kwargs.items()): setattr(self, k, v) @property def title(self): """A human-readable title for the playlist. Optional""" return self._title @title.setter def title(self, title): self._title = title @property def creator(self): """Human-readable name of the entity (author, authors, group, company, etc) that authored the playlist. Optional""" return self._creator @creator.setter def creator(self, creator): self._creator = creator @property def annotation(self): """A human-readable comment on the playlist. This is character data, not HTML, and it may not contain markup. Optional""" return self._annotation @annotation.setter def annotation(self, annotation): self._annotation = annotation @property def info(self): """URI of a web page to find out more about this playlist. Optional""" return self._info @info.setter def info(self, info): self._info = info @property def location(self): """Source URI for this playlist. Optional""" return self._location @location.setter def location(self, location): self._location = location @property def identifier(self): """Canonical ID for this playlist. Likely to be a hash or other location-independent name. Optional""" return self._identifier @identifier.setter def identifier(self, identifier): self._identifier = identifier @property def image(self): """URI of an image to display in the absence of a trackList/image element. Optional""" return self._image @image.setter def image(self, image): self._image = image @property def date(self): """Creation date (not last-modified date) of the playlist. Optional""" return self._date @date.setter def date(self, date): self._date = date @property def license(self): """URI of a resource that describes the license under which this playlist was released. Optional""" return self._license @license.setter def license(self, license): self._license = license @property def meta(self): return self._meta def add_meta(self, key, value): """Add a meta element to the playlist.""" self._meta[key] = value def del_meta(self, key): """Remove a meta element.""" del self._meta[key] def add_link(self, key, value): """Add a link element to the playlist.""" self._link[key] = value def del_link(self, key): """Remove a link element.""" del self._link[key] def add_attribution(self, location, identifier): self.attrbutions.append((location, identifier)) def truncate_attributions(self, numattributions): self.attrbutions = self.attributions[-numattributions:] # Todo: Attribution, Link, Meta, Extension def add_extension(self, application): pass def make_extension_element(self, namespace, name, attributes, value): pass def remove_extension(self, application): pass @property def track(self): return self._trackList @track.setter def track(self, track): self.add_track(track) def add_track(self, track={}, **kwargs): if isinstance(track, list): for t in track: self.add_track(t) elif isinstance(track, Track): self._trackList.append(track) elif isinstance(track, dict) and len(track) > 0: self._trackList.append(Track(track)) elif len(kwargs) > 0: self._trackList.append(Track(kwargs)) def add_tracks(self, tracks): for t in tracks: self.add_track(t) def toXml(self, encoding="utf-8", pretty_print=True): root = ET.Element("{{{0}}}playlist".format(self.NS)) root.set("version", self.version) self._addAttributesToXml(root, ["title", "info", "creator", "annotation", "location", "identifier", "image", "date", "license"]) self._addDictionaryElements(root, "link", self._link) self._addDictionaryElements(root, "meta", self._meta) if len(self._trackList): track_list = ET.SubElement(root, "{{{0}}}trackList".format(self.NS)) for track in self._trackList: track_list = track.getXmlObject(track_list) if pretty_print: indent(root) return ET.tostring(root, encoding) class Track(XspfBase): def __init__(self, obj={}, **kwargs): self._location = "" self._identifier = "" self._title = "" self._creator = "" self._annotation = "" self._info = "" self._image = "" self._album = "" self._trackNum = "" self._duration = "" self._link = {} self._meta = {} if len(obj): for k, v in list(obj.items()): setattr(self, k, v) if len(kwargs): for k, v in list(kwargs.items()): setattr(self, k, v) @property def location(self): """URI of resource to be rendered. Probably an audio resource, but MAY be any type of resource with a well-known duration. Zero or more""" return self._location @location.setter def location(self, location): self._location = location @property def identifier(self): """ID for this resource. Likely to be a hash or other location-independent name, such as a MusicBrainz identifier. MUST be a legal URI. Zero or more""" return self._identifier @identifier.setter def identifier(self, identifier): self._identifier = identifier @property def title(self): """Human-readable name of the track that authored the resource which defines the duration of track rendering. Optional""" return self._title @title.setter def title(self, title): self._title = title @property def creator(self): """Human-readable name of the entity (author, authors, group, company, etc) that authored the resource which defines the duration of track rendering.""" return self._creator @creator.setter def creator(self, creator): self._creator = creator @property def annotation(self): """A human-readable comment on the track. This is character data, not HTML, and it may not contain markup.""" return self._annotation @annotation.setter def annotation(self, annotation): self._annotation = annotation @property def info(self): """URI of a place where this resource can be bought or more info can be found. Optional""" return self._info @info.setter def info(self, info): self._info = info @property def image(self): """URI of an image to display for the duration of the track. Optional""" return self._image @image.setter def image(self, image): self._image = image @property def album(self): """Human-readable name of the collection from which the resource which defines the duration of track rendering comes. Optional""" return self._album @album.setter def album(self, album): self._album = album @property def trackNum(self): """Integer with value greater than zero giving the ordinal position of the media on the album. Optional""" return self._trackNum @trackNum.setter def trackNum(self, trackNum): self._trackNum = trackNum @property def duration(self): """The time to render a resource, in milliseconds. Optional""" return self._duration @duration.setter def duration(self, duration): self._duration = duration @property def meta(self): return self._meta def add_meta(self, key, value): """Add a meta element to the playlist.""" self._meta[key] = value def del_meta(self, key): """Remove a meta element.""" del self._meta[key] def add_link(self, key, value): """Add a link element to the playlist.""" self._link[key] = value def del_link(self, key): """Remove a link element.""" del self._link[key] # Todo: Link, Meta, Extension def getXmlObject(self, parent): track = ET.SubElement(parent, "{{{0}}}track".format(self.NS)) self._addAttributesToXml(track, ["location", "identifier", "title", "creator", "annotation", "info", "image", "album", "trackNum", "duration"]) self._addDictionaryElements(track, "link", self._link) self._addDictionaryElements(track, "meta", self._meta) return parent Spiff = Xspf