diff options
author | Anton Khirnov <wyskas@gmail.com> | 2009-08-24 20:46:27 +0200 |
---|---|---|
committer | Anton Khirnov <wyskas@gmail.com> | 2009-08-24 20:46:27 +0200 |
commit | e4f46487145be0bbcd2aa278388a1b4b3ef78f54 (patch) | |
tree | 4e0170664bdc651c782601544572170d20090239 /nephilim/song.py | |
parent | e7c01e7b53f02b24e6938d945e84453537f3fa31 (diff) |
song: convert to a subclass of dict.
also don't store whole song in Library and Playlist,
this saves a significant amount of memory.
Diffstat (limited to 'nephilim/song.py')
-rw-r--r-- | nephilim/song.py | 136 |
1 files changed, 79 insertions, 57 deletions
diff --git a/nephilim/song.py b/nephilim/song.py index 0f1a627..c9c1145 100644 --- a/nephilim/song.py +++ b/nephilim/song.py @@ -1,5 +1,4 @@ # -# Copyright (C) 2008 jerous <jerous@gmail.com> # Copyright (C) 2009 Anton Khirnov <wyskas@gmail.com> # # Nephilim is free software: you can redistribute it and/or modify @@ -21,80 +20,103 @@ import os from common import sec2min -class Song: - """The Song class offers an abstraction of a song.""" - _data = None +class Song(dict): + """Song provides a dictionary-like wrapper around song metadata. + Its instances _shouldn't_ be stored, use SongRef or PlaylistEntryRef for that.""" def __init__(self, data): - self._data = data - if 'track' in self._data: - try: - self._data['track'] = int(self._data['track']) - except ValueError: - # OGG tracks come in #track/#total format - try: - self._data['track'] = int(self._data['track'].split('/')[0]) - except ValueError: - self._data['track'] = 0 + # decode all strings to unicode + for tag in data: + if isinstance(data[tag], str): + data[tag] = data[tag].decode('utf-8') - # ensure all string-values are utf-8 encoded - for tag in self._data.keys(): - if isinstance(self._data[tag], str): - self._data[tag] = self._data[tag].decode('utf-8') - if 'time' in self._data: - self._data['time'] = int(self._data['time']) - self._data['timems'] = '%i:%i'%(self._data['time'] / 60, self._data['time'] % 60) - self._data['length'] = sec2min(self._data['time']) + dict.__init__(self, data) def __eq__(self, other): if not isinstance(other, Song): return NotImplemented - return self._data['file'] == other._data['file'] + return self['file'] == other['file'] def __ne__(self, other): if not isinstance(other, Song): return NotImplemented - return self._data['file'] != other._data['file'] + return self['file'] != other['file'] - def id(self): - """Get the song's playlist ID. (-1 if not in playlist).""" - return self.tag('id', '-1') + def __getitem__(self, key): + try: + return dict.__getitem__(self, key) + except KeyError: + if key == 'tracknum': + try: + return int(self['track']) + except ValueError: + try: + return int(self['track'].split('/')[0]) + except ValueError: + return 0 + elif key == 'length': + return sec2min(int(self['time'])) + elif key == 'id': + return '-1' + elif key == 'title': + return self['file'] + return '' + + def __nonzero__(self): + return bool(self['file']) - def title(self): - """Get the song's title (or filename if it has no title).""" - return self.tag('title', self._data['file']) + def expand_tags(self, str): + """Expands tags in form $tag in str.""" + ret = str + ret = ret.replace('$title', self['title']) #to ensure that it is set to at least filename + for tag in self.keys() + ['tracknum', 'length', 'id']: + ret = ret.replace('$' + tag, unicode(self[tag])) + ret = ret.replace('$songdir', os.path.dirname(self['file'])) + return ret - def artist(self): - """Get the song's artist.""" - return self.tag('artist') - def track(self): - """Get the song's track number.""" - return self.tag('track') +class SongRef: + """SongRef stores only a reference to the song (uri) instead + of full metadata to conserve memory. Song's tags can be accessed + as in Song, but it requires a call to MPD for each tag. """ + path = None + mpclient = None - def album(self): - """Get the album.""" - return self.tag('album') + def __init__(self, mpclient, path): + self.mpclient = mpclient + self.path = path - def filepath(self): - """Get the filepath.""" - return self._data['file'] + def __getitem__(self, key): + return self.song()[key] - def tag(self, tag, default=''): - """Get a tag. If it doesn't exist, return default.""" - if tag in self._data: - return self._data[tag] - if tag=='song': - return self.__str__() + def __nonzero__(self): + return bool(self.path) - return default + def song(self): + try: + return Song(self.mpclient.find('file', self.path)[0]) + except IndexError: + return Song({}) - def expand_tags(self, str): - """Expands tags in form $tag in str.""" - ret = str - ret = ret.replace('$title', self.title()) #to ensure that it is set to at least filename - for tag in self._data: - ret = ret.replace('$' + tag, unicode(self._data[tag])) - ret = ret.replace('$songdir', os.path.dirname(self.filepath())) - return ret +class PlaylistEntryRef: + """This class stores a reference to a playlist entry instead of full + metadata to conserve memory. Song's tags can be accessed + as in Song, but it requires a call to MPD for each tag. """ + id = None + mpclient = None + + def __init__(self, mpclient, id): + self.mpclient = mpclient + self.id = id + + def __getitem__(self, key): + return self.song()[key] + + def __nonzero__(self): + return self.id != '-1' + def song(self): + try: + return Song(self.mpclient.playlistid(id)[0]) + except IndexError: + return Song({}) |