summaryrefslogtreecommitdiff
path: root/nephilim/song.py
blob: 607d4a6d1e63e0279eba321e4bf636b6945890f8 (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
#
#    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 common import sec2min

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):
        # decode all strings to unicode
        for tag in data:
            if isinstance(data[tag], list):
                data[tag] = ','.join(data[tag])
            if isinstance(data[tag], str):
                data[tag] = data[tag].decode('utf-8')

        dict.__init__(self, data)

    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):
        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']
            elif key == 'albumartist':
                return self['artist']
            return ''

    def __nonzero__(self):
        return bool(self['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

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):
        try:
            return Song(self.mpclient.find('file', self.path)[0])
        except IndexError:
            return Song({})

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):
        try:
            return self.mpclient.playlistid(self.plid)
        except IndexError:
            return Song({})