]> kaliko git repositories - mpd-sima.git/blob - sima/lib/track.py
Exposes genres in Track objets
[mpd-sima.git] / sima / lib / track.py
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2009-2021 kaliko <kaliko@azylum.org>
4 # Copyright (c) 2009 J. Alexander Treuman (Tag collapse method)
5 # Copyright (c) 2008 Rick van Hattem
6 #
7 #  This file is part of sima
8 #
9 #  sima is free software: you can redistribute it and/or modify
10 #  it under the terms of the GNU General Public License as published by
11 #  the Free Software Foundation, either version 3 of the License, or
12 #  (at your option) any later version.
13 #
14 #  sima is distributed in the hope that it will be useful,
15 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
16 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 #  GNU General Public License for more details.
18 #
19 #  You should have received a copy of the GNU General Public License
20 #  along with sima.  If not, see <http://www.gnu.org/licenses/>.
21 #
22 #
23
24 import time
25
26 from .meta import Artist, SEPARATOR
27
28 class Track:
29     """
30     Track object.
31     Instantiate with Player replies.
32
33     :param str file: media file, defaults to ``None``
34     :param int time: duration in second, defaults to 0
35     :param int pos: position in queue, defaults to -1
36     :param str title|artist|album: defaults to ""
37     :param str musicbrainz_artistid|musicbrainz_albumartistid: MusicBrainz IDs, defaults to ``None``
38     """
39
40     def __init__(self, file=None, time=0, pos=-1, **kwargs):
41         self.title = self.artist = self.album = self.albumartist = ''
42         self.musicbrainz_artistid = self.musicbrainz_albumartistid = None
43         self.pos = int(pos)
44         self._file = file
45         self._empty = False
46         self._time = time
47         if not kwargs:
48             self._empty = True
49         self.__dict__.update(**kwargs)
50         self.tags_to_collapse = ['artist', 'album', 'title', 'date',
51                                  'genre', 'albumartist',
52                                  'musicbrainz_artistid',
53                                  'musicbrainz_albumartistid']
54         #  have tags been collapsed?
55         self.collapsed_tags = list()
56         # Needed for multiple tags which returns a list instead of a string
57         self._collapse_tags()
58
59     def _collapse_tags(self):
60         """
61         Necessary to deal with tags defined multiple times.
62         These entries are set as lists instead of strings.
63         """
64         for tag, value in self.__dict__.items():
65             if tag not in self.tags_to_collapse:
66                 continue
67             if isinstance(value, list):
68                 self.collapsed_tags.append(tag)
69                 self.__dict__.update({tag: SEPARATOR.join(value)})
70
71     def __repr__(self):
72         return '%s(artist="%s", album="%s", title="%s", file="%s")' % (
73             self.__class__.__name__,
74             self.artist,
75             self.album,
76             self.title,
77             self.file,
78         )
79
80     def __str__(self):
81         return '{artist} - {album} - {title} ({length})'.format(
82                 length=self.length,
83                 **self.__dict__
84                 )
85
86     def __int__(self):
87         return self.time
88
89     def __add__(self, other):
90         return Track(time=self.time + other.time)
91
92     def __sub__(self, other):
93         return Track(time=self.time - other.time)
94
95     def __hash__(self):
96         if self.file:
97             return hash(self.file)
98         return id(self)
99
100     def __eq__(self, other):
101         return hash(self) == hash(other)
102
103     def __ne__(self, other):
104         return hash(self) != hash(other)
105
106     def __bool__(self):
107         if not self._file:
108             return False
109         return not self._empty
110
111     @property
112     def file(self):
113         """file is an immutable attribute that's used for the hash method"""
114         return self._file
115
116     def get_time(self):
117         """get time property"""
118         return self._time
119
120     def set_time(self, value):
121         """set time property"""
122         self._time = int(value)
123
124     time = property(get_time, set_time, doc='song duration in seconds (use :attr:`length` for human readable time)')
125
126     @property
127     def length(self):
128         """Get a fancy duration as ``%H:%M:%S`` (use :attr:`time` to get duration in second only)"""
129         temps = time.gmtime(int(self.time))  #TODO: returns a date not a duration
130         if temps.tm_hour:
131             fmt = '%H:%M:%S'
132         else:
133             fmt = '%M:%S'
134         return time.strftime(fmt, temps)
135
136     @property
137     def genres(self):
138         """Fetches Genres for the track
139         Multivalue genre are dealt with:
140           * when genre tag is multivalued
141           * when single tag uses coma or semi-colon separator
142         """
143         if 'genre' not in self.__dict__:
144             return []
145         genres = self.genre.split(SEPARATOR)
146         for sep in [',', ';']:
147             if sep in self.genre:
148                 genres = [g for multi in genres for g in multi.split(sep) if g]
149         return list(map(str.strip, genres))
150
151     @property
152     def Artist(self):
153         """Get the :class:`sima.lib.meta.Artist` associated to this track"""
154         if not self.artist:
155             if not self.musicbrainz_artistid:
156                 return Artist(name='[unknown]',
157                               mbid='125ec42a-7229-4250-afc5-e057484327fe')
158             return Artist(name='[unknown]', **self.__dict__)
159         return Artist(**self.__dict__)
160
161 # VIM MODLINE
162 # vim: ai ts=4 sw=4 sts=4 expandtab