231 lines
6.3 KiB
Python
231 lines
6.3 KiB
Python
from collections import namedtuple
|
|
|
|
class ImmutableObject(object):
|
|
"""
|
|
Superclass for immutable objects whose fields can only be modified via the
|
|
constructor.
|
|
|
|
:param kwargs: kwargs to set as fields on the object
|
|
:type kwargs: any
|
|
"""
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
for key, value in kwargs.items():
|
|
if not hasattr(self, key):
|
|
raise TypeError('__init__() got an unexpected keyword ' + \
|
|
'argument \'%s\'' % key)
|
|
self.__dict__[key] = value
|
|
|
|
def __setattr__(self, name, value):
|
|
if name.startswith('_'):
|
|
return super(ImmutableObject, self).__setattr__(name, value)
|
|
raise AttributeError('Object is immutable.')
|
|
|
|
def __repr__(self):
|
|
kwarg_pairs = []
|
|
for (key, value) in sorted(self.__dict__.items()):
|
|
if isinstance(value, (frozenset, tuple)):
|
|
value = list(value)
|
|
kwarg_pairs.append('%s=%s' % (key, repr(value)))
|
|
return '%(classname)s(%(kwargs)s)' % {
|
|
'classname': self.__class__.__name__,
|
|
'kwargs': ', '.join(kwarg_pairs),
|
|
}
|
|
|
|
def __hash__(self):
|
|
hash_sum = 0
|
|
for key, value in self.__dict__.items():
|
|
hash_sum += hash(key) + hash(value)
|
|
return hash_sum
|
|
|
|
def __eq__(self, other):
|
|
if not isinstance(other, self.__class__):
|
|
return False
|
|
|
|
return self.__dict__ == other.__dict__
|
|
|
|
def __ne__(self, other):
|
|
return not self.__eq__(other)
|
|
|
|
def copy(self, **values):
|
|
"""
|
|
Copy the model with ``field`` updated to new value.
|
|
|
|
Examples::
|
|
|
|
# Returns a track with a new name
|
|
Track(name='foo').copy(name='bar')
|
|
# Return an album with a new number of tracks
|
|
Album(num_tracks=2).copy(num_tracks=5)
|
|
|
|
:param values: the model fields to modify
|
|
:type values: dict
|
|
:rtype: new instance of the model being copied
|
|
"""
|
|
data = {}
|
|
for key in self.__dict__.keys():
|
|
public_key = key.lstrip('_')
|
|
data[public_key] = values.pop(public_key, self.__dict__[key])
|
|
for key in values.keys():
|
|
if hasattr(self, key):
|
|
data[key] = values.pop(key)
|
|
if values:
|
|
raise TypeError("copy() got an unexpected keyword argument '%s'"
|
|
% key)
|
|
return self.__class__(**data)
|
|
|
|
|
|
class Artist(ImmutableObject):
|
|
"""
|
|
:param uri: artist URI
|
|
:type uri: string
|
|
:param name: artist name
|
|
:type name: string
|
|
:param musicbrainz_id: MusicBrainz ID
|
|
:type musicbrainz_id: string
|
|
"""
|
|
|
|
#: The artist URI. Read-only.
|
|
uri = None
|
|
|
|
#: The artist name. Read-only.
|
|
name = None
|
|
|
|
#: The MusicBrainz ID of the artist. Read-only.
|
|
musicbrainz_id = None
|
|
|
|
|
|
class Album(ImmutableObject):
|
|
"""
|
|
:param uri: album URI
|
|
:type uri: string
|
|
:param name: album name
|
|
:type name: string
|
|
:param artists: album artists
|
|
:type artists: list of :class:`Artist`
|
|
:param num_tracks: number of tracks in album
|
|
:type num_tracks: integer
|
|
:param musicbrainz_id: MusicBrainz ID
|
|
:type musicbrainz_id: string
|
|
"""
|
|
|
|
#: The album URI. Read-only.
|
|
uri = None
|
|
|
|
#: The album name. Read-only.
|
|
name = None
|
|
|
|
#: A set of album artists. Read-only.
|
|
artists = frozenset()
|
|
|
|
#: The number of tracks in the album. Read-only.
|
|
num_tracks = 0
|
|
|
|
#: The MusicBrainz ID of the album. Read-only.
|
|
musicbrainz_id = None
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
self.__dict__['artists'] = frozenset(kwargs.pop('artists', []))
|
|
super(Album, self).__init__(*args, **kwargs)
|
|
|
|
|
|
CpTrack = namedtuple('CpTrack', ['cpid', 'track'])
|
|
|
|
|
|
class Track(ImmutableObject):
|
|
"""
|
|
:param uri: track URI
|
|
:type uri: string
|
|
:param name: track name
|
|
:type name: string
|
|
:param artists: track artists
|
|
:type artists: list of :class:`Artist`
|
|
:param album: track album
|
|
:type album: :class:`Album`
|
|
:param track_no: track number in album
|
|
:type track_no: integer
|
|
:param date: track release date
|
|
:type date: :class:`datetime.date`
|
|
:param length: track length in milliseconds
|
|
:type length: integer
|
|
:param bitrate: bitrate in kbit/s
|
|
:type bitrate: integer
|
|
:param musicbrainz_id: MusicBrainz ID
|
|
:type musicbrainz_id: string
|
|
"""
|
|
|
|
#: The track URI. Read-only.
|
|
uri = None
|
|
|
|
#: The track name. Read-only.
|
|
name = None
|
|
|
|
#: A set of track artists. Read-only.
|
|
artists = frozenset()
|
|
|
|
#: The track :class:`Album`. Read-only.
|
|
album = None
|
|
|
|
#: The track number in album. Read-only.
|
|
track_no = 0
|
|
|
|
#: The track release date. Read-only.
|
|
date = None
|
|
|
|
#: The track length in milliseconds. Read-only.
|
|
length = None
|
|
|
|
#: The track's bitrate in kbit/s. Read-only.
|
|
bitrate = None
|
|
|
|
#: The MusicBrainz ID of the track. Read-only.
|
|
musicbrainz_id = None
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
self.__dict__['artists'] = frozenset(kwargs.pop('artists', []))
|
|
super(Track, self).__init__(*args, **kwargs)
|
|
|
|
def mpd_format(self, *args, **kwargs):
|
|
from mopidy.frontends.mpd import translator
|
|
return translator.track_to_mpd_format(self, *args, **kwargs)
|
|
|
|
|
|
class Playlist(ImmutableObject):
|
|
"""
|
|
:param uri: playlist URI
|
|
:type uri: string
|
|
:param name: playlist name
|
|
:type name: string
|
|
:param tracks: playlist's tracks
|
|
:type tracks: list of :class:`Track` elements
|
|
:param last_modified: playlist's modification time
|
|
:type last_modified: :class:`datetime.datetime`
|
|
"""
|
|
|
|
#: The playlist URI. Read-only.
|
|
uri = None
|
|
|
|
#: The playlist name. Read-only.
|
|
name = None
|
|
|
|
#: The playlist's tracks. Read-only.
|
|
tracks = tuple()
|
|
|
|
#: The playlist modification time. Read-only.
|
|
#:
|
|
#: :class:`datetime.datetime`, or :class:`None` if unknown.
|
|
last_modified = None
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
self.__dict__['tracks'] = tuple(kwargs.pop('tracks', []))
|
|
super(Playlist, self).__init__(*args, **kwargs)
|
|
|
|
@property
|
|
def length(self):
|
|
"""The number of tracks in the playlist. Read-only."""
|
|
return len(self.tracks)
|
|
|
|
def mpd_format(self, *args, **kwargs):
|
|
from mopidy.frontends.mpd import translator
|
|
return translator.playlist_to_mpd_format(self, *args, **kwargs)
|