123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115 |
- import os
- import urllib.parse
- import xml.etree.ElementTree
- import dateutil.parser
- class XmlDict:
- def __init__(self, node):
- assert isinstance(node, xml.etree.ElementTree.Element), node
- assert node.tag == "dict", node.tag
- self._node = node
- def _get_value_node(self, key):
- # WORKAROUND method getnext() is sadly not available
- for child_idx, child_node in enumerate(self._node):
- if child_node.tag == "key" and child_node.text == key:
- return self._node[child_idx + 1]
- raise KeyError(key)
- @staticmethod
- def _parse_value_node(value_node):
- if value_node.tag == "string":
- return value_node.text
- if value_node.tag == "integer":
- return int(value_node.text)
- if value_node.tag == "date":
- return dateutil.parser.parse(value_node.text)
- if value_node.tag == "dict":
- return XmlDict(value_node)
- raise ValueError(value_node.tag)
- def __getitem__(self, key):
- value_node = self._get_value_node(key)
- return XmlDict._parse_value_node(value_node)
- def get(self, key, default):
- try:
- return self[key]
- except KeyError:
- return default
- def items(self):
- for key_node, value_node in zip(*[iter(self._node)] * 2):
- assert key_node.tag == "key"
- key = key_node.text
- value = XmlDict._parse_value_node(value_node)
- yield (key, value)
- def values(self):
- for _, value in self.items():
- yield value
- class Track:
- LOCAL_LOCATION_URL_PREFIX = "file://localhost/"
- def __init__(self, track_dict):
- assert isinstance(track_dict, XmlDict)
- self._dict = track_dict
- self._id = self._dict["Track ID"]
- assert isinstance(self._id, int)
- self._location_url = self._dict.get("Location", None)
- self._play_count = self._dict.get("Play Count", 0)
- assert isinstance(self._play_count, int)
- self._last_play_dt = self._dict.get("Play Date UTC", None)
- @property
- def track_id(self):
- return self._id
- @property
- def location_url(self):
- return self._location_url
- @property
- def local(self):
- return self.location_url is not None and self.location_url.startswith(
- self.LOCAL_LOCATION_URL_PREFIX
- )
- @property
- def local_path(self):
- if self.local:
- return os.path.sep + urllib.parse.unquote(
- self.location_url[len(self.LOCAL_LOCATION_URL_PREFIX) :],
- )
- raise ValueError(self.location_url)
- @property
- def play_count(self):
- return self._play_count
- @property
- def last_play_dt(self):
- return self._last_play_dt
- class XmlLibrary:
- def __init__(self, path):
- self._tree = xml.etree.ElementTree.parse(path)
- self._root_dict = XmlDict(self._tree.find("./dict"))
- self._id = self._root_dict["Library Persistent ID"]
- assert isinstance(self._id, str), self._id
- assert len(self._id) > 4
- @property
- def library_id(self):
- return self._id
- @property
- def tracks(self):
- for track_dict in self._root_dict["Tracks"].values():
- yield Track(track_dict)
|