summaryrefslogtreecommitdiff
path: root/nephilim/song.py
diff options
context:
space:
mode:
authorAnton Khirnov <wyskas@gmail.com>2009-08-24 20:46:27 +0200
committerAnton Khirnov <wyskas@gmail.com>2009-08-24 20:46:27 +0200
commite4f46487145be0bbcd2aa278388a1b4b3ef78f54 (patch)
tree4e0170664bdc651c782601544572170d20090239 /nephilim/song.py
parente7c01e7b53f02b24e6938d945e84453537f3fa31 (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.py136
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({})