mpd: better error handling on connection failure
[nephilim.git] / nephilim / song.py
blobcf2d157eb637abaa93a4e7eef1f2a3c74fa947ef
2 # Copyright (C) 2009 Anton Khirnov <wyskas@gmail.com>
4 # Nephilim is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation, either version 3 of the License, or
7 # (at your option) any later version.
9 # Nephilim is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with Nephilim. If not, see <http://www.gnu.org/licenses/>.
18 from PyQt4 import QtCore
19 import os
21 from common import sec2min
23 class Song(dict):
24 """Song provides a dictionary-like wrapper around song metadata.
25 Its instances _shouldn't_ be stored, use SongRef or PlaylistEntryRef for that."""
27 def __init__(self, data):
28 # decode all strings to unicode
29 for tag in data:
30 if isinstance(data[tag], list):
31 data[tag] = ','.join(data[tag])
32 if isinstance(data[tag], str):
33 data[tag] = data[tag].decode('utf-8')
35 dict.__init__(self, data)
37 def __eq__(self, other):
38 if not isinstance(other, Song):
39 return NotImplemented
40 return self['file'] == other['file']
42 def __ne__(self, other):
43 if not isinstance(other, Song):
44 return NotImplemented
45 return self['file'] != other['file']
47 def __getitem__(self, key):
48 try:
49 return dict.__getitem__(self, key)
50 except KeyError:
51 if key == 'tracknum':
52 try:
53 return int(self['track'])
54 except ValueError:
55 try:
56 return int(self['track'].split('/')[0])
57 except ValueError:
58 return 0
59 elif key == 'length':
60 return sec2min(int(self['time']))
61 elif key == 'id':
62 return '-1'
63 elif key == 'title':
64 return self['file']
65 elif key == 'albumartist':
66 return self['artist']
67 return ''
69 def __nonzero__(self):
70 return bool(self['file'])
72 def expand_tags(self, str):
73 """Expands tags in form $tag in str."""
74 ret = str
75 ret = ret.replace('$title', self['title']) #to ensure that it is set to at least filename
76 for tag in self.keys() + ['tracknum', 'length', 'id']:
77 ret = ret.replace('$' + tag, unicode(self[tag]))
78 ret = ret.replace('$songdir', os.path.dirname(self['file']))
79 return ret
81 class SongRef:
82 """SongRef stores only a reference to the song (uri) instead
83 of full metadata to conserve memory. Song's tags can be accessed
84 as in Song, but it requires a call to MPD for each tag. """
85 path = None
86 mpclient = None
88 def __init__(self, mpclient, path):
89 self.mpclient = mpclient
90 self.path = path
92 def __getitem__(self, key):
93 return self.song()[key]
95 def __nonzero__(self):
96 return bool(self.path)
98 def song(self):
99 try:
100 return Song(self.mpclient.find('file', self.path)[0])
101 except IndexError:
102 return Song({})
104 class PlaylistEntryRef:
105 """This class stores a reference to a playlist entry instead of full
106 metadata to conserve memory. Song's tags can be accessed
107 as in Song, but it requires a call to MPD for each tag. """
108 id = None
109 mpclient = None
111 def __init__(self, mpclient, id):
112 self.mpclient = mpclient
113 self.id = id
115 def __getitem__(self, key):
116 return self.song()[key]
118 def __nonzero__(self):
119 return self.id != '-1'
121 def song(self):
122 try:
123 return Song(self.mpclient.playlistid(id)[0])
124 except IndexError:
125 return Song({})