summaryrefslogtreecommitdiff
path: root/nephilim/song.py
blob: eb5fedb2585dcfed47da1270a378d0943aaec083 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#
#    Copyright (C) 2009 Anton Khirnov <wyskas@gmail.com>
#
#    Nephilim is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    Nephilim is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with Nephilim.  If not, see <http://www.gnu.org/licenses/>.
#

from PyQt4 import QtCore
import os
from   string import Template

from common import sec2min

class Song(dict):
    """
    Song provides a dictionary-like wrapper around song metadata.
    Its instances are generated by MPClient, don't instantiate it
    directly.
    Its instances _shouldn't_ be stored, use SongRef or PlaylistEntryRef for that.
    Access '?tag' to get always at least an empty string.
    """

    #### PUBLIC ####
    def __init__(self, data = {}):
        dict.__init__(self)
        for key in data:
            dict.__setitem__(self, key.lower(), data[key])
        if not 'file' in self:
            self['file'] = ''
    def __eq__(self, other):
        if not isinstance(other, Song):
            return NotImplemented
        return self['file'] == other['file']
    def __ne__(self, other):
        if not isinstance(other, Song):
            return NotImplemented
        return self['file'] != other['file']
    def __getitem__(self, key):
        key = key.lower()
        try:
            return dict.__getitem__(self, key)
        except KeyError:
            if key and key[0] == '?':
                key = key[1:]
                return self[key] if key in self else ''
            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']
            elif key == 'albumartist':
                return self['artist']
            elif key == 'songdir':
                return os.path.dirname(self['file'])

            raise KeyError
    def __contains__(self, item):
        if dict.__contains__(self, item.lower()):
            return True
        try:
            self[item]
            return True
        except KeyError:
            return False
    def __nonzero__(self):
        return bool(self['file'])

    def expand_tags(self, s):
        """Expands tags in form ${tag} in string s."""
        ret = Template(s)
        ret = ret.safe_substitute(self)
        return ret

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 __init__(self, mpclient, path):
        self.mpclient = mpclient
        self.path     = path

    def __getitem__(self, key):
        return self.song()[key]

    def __nonzero__(self):
        return bool(self.path)

    def song(self):
        return list(self.mpclient.find_sync('file', self.path))[0]

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. """
    plid     = None
    mpclient = None

    def __init__(self, mpclient, plid):
        self.mpclient = mpclient
        self.plid       = plid

    def __getitem__(self, key):
        return self.plid if key == 'id' else self.song()[key]

    def __nonzero__(self):
        return self.plid != '-1'

    def song(self):
        return self.mpclient.get_plist_song(self.plid)