yading@7
|
1 """
|
yading@7
|
2 This module contains higher level classes to query Musixmatch API and build
|
yading@7
|
3 simple dictionary-like objects representing a Track or a TracksCollection.
|
yading@7
|
4
|
yading@7
|
5 >>> from musixmatch.track import Track, TracksCollection
|
yading@7
|
6 >>> import musixmatch.api
|
yading@7
|
7 >>>
|
yading@7
|
8 >>> try:
|
yading@7
|
9 ... track = Track(track_mbid=8976)
|
yading@7
|
10 ... collection = TracksCollection.fromChart(country='us', page=1)
|
yading@7
|
11 ... except musixmatch.api.Error, e:
|
yading@7
|
12 ... pass
|
yading@7
|
13 """
|
yading@7
|
14 import musixmatch
|
yading@7
|
15 __license__ = musixmatch.__license__
|
yading@7
|
16 __author__ = musixmatch.__author__
|
yading@7
|
17
|
yading@7
|
18 from musixmatch import api, lyrics, subtitle
|
yading@7
|
19 from musixmatch.base import Item, ItemsCollection
|
yading@7
|
20 from musixmatch.ws import track, matcher, album
|
yading@7
|
21
|
yading@7
|
22 _marker=object()
|
yading@7
|
23
|
yading@7
|
24 class Track(Item):
|
yading@7
|
25 """
|
yading@7
|
26 This class builds a :py:class:`dict` like object representing a track. It
|
yading@7
|
27 can get track information through the :py:class:`musixmatch.api.Method`
|
yading@7
|
28 **track.get** or from an already well-formed :py:class:`dict`. Create a
|
yading@7
|
29 Track object based on a given keyword argument:
|
yading@7
|
30
|
yading@7
|
31 :param track_id: musiXmatch track ID
|
yading@7
|
32 :param musicbrainz_id: Musicbrainz track ID
|
yading@7
|
33 :param track_echonest_id: Echonest track ID
|
yading@7
|
34 :param track_data: an already well-formed :py:class:`dict` of track data
|
yading@7
|
35 :raises: :py:exc:`musixmatch.api.Error` if :py:class:`musixmatch.api.ResponseStatusCode` is not 200
|
yading@7
|
36
|
yading@7
|
37 Once information are collected, the following keys are available:
|
yading@7
|
38
|
yading@7
|
39 :keyword track_id: musiXmatch track ID
|
yading@7
|
40 :keyword track_mbid: Musicbrainz track ID
|
yading@7
|
41 :keyword lyrics_id: musiXmatch lyrics ID
|
yading@7
|
42 :keyword instrumental: wether the track is instrumental or not
|
yading@7
|
43 :keyword subtitle_id: musiXmatch subtitle ID
|
yading@7
|
44 :keyword track_name: track name
|
yading@7
|
45 :keyword album_coverart_100x100: album cover URL
|
yading@7
|
46 :keyword artist_id: musiXmatch artist ID
|
yading@7
|
47 :keyword artist_mbid: Musicbrainz artist ID
|
yading@7
|
48 :keyword artist_name: artist name
|
yading@7
|
49
|
yading@7
|
50 Keyword access have been overloaded thanks to the :py:meth:`get` method
|
yading@7
|
51 which will eventually fetch the matching lyrics or subtitle.
|
yading@7
|
52 """
|
yading@7
|
53 __api_method__ = track.get
|
yading@7
|
54
|
yading@7
|
55 @classmethod
|
yading@7
|
56 def fromMatcher(cls, **keywords):
|
yading@7
|
57 """
|
yading@7
|
58 Returns a :py:class:`Track` based on the result of the
|
yading@7
|
59 :py:class:`musiXmatch.api.Method` **matcher.track.get**. Accepts the
|
yading@7
|
60 following keywords:
|
yading@7
|
61
|
yading@7
|
62 :param q_track: words to be searched among track titles
|
yading@7
|
63 :param q_artist: words to be searched among artist names
|
yading@7
|
64 """
|
yading@7
|
65 return cls.fromResponseMessage(matcher.track.get(**keywords))
|
yading@7
|
66
|
yading@7
|
67 def get(self, key, default=_marker):
|
yading@7
|
68 """
|
yading@7
|
69 If key is *lyrics* or *subtitle* try to query api for proper value,
|
yading@7
|
70 and build an :py:class:`musixmatch.lyrics.Lyrics` or
|
yading@7
|
71 :py:class:`musixmatch.subtitle.Subtitle`. Access to the above mentioned
|
yading@7
|
72 keys may fail with :py:exc:`musixmatch.api.Error`. Once fetched, the
|
yading@7
|
73 result is saved.
|
yading@7
|
74 """
|
yading@7
|
75 special = {
|
yading@7
|
76 'lyrics': lyrics.Lyrics,
|
yading@7
|
77 'subtitle': subtitle.Subtitle,
|
yading@7
|
78 }
|
yading@7
|
79 if key in special and not key in self:
|
yading@7
|
80 self[key] = special[key](track_id=self['track_id'])
|
yading@7
|
81 value = dict.get(self, key, default)
|
yading@7
|
82 if value == _marker:
|
yading@7
|
83 raise KeyError, key
|
yading@7
|
84 return value
|
yading@7
|
85
|
yading@7
|
86 def __getitem__(self, key):
|
yading@7
|
87 return self.get(key)
|
yading@7
|
88
|
yading@7
|
89 def postFeedback(self, feedback):
|
yading@7
|
90 """
|
yading@7
|
91 Post feedback about lyrics for this track. **feedback** can be one of:
|
yading@7
|
92
|
yading@7
|
93 :keyword wrong_attribution: the lyrics shown are not by the artist that
|
yading@7
|
94 I selected.
|
yading@7
|
95 :keyword bad_characters: there are strange characters and/or words
|
yading@7
|
96 that are partially scrambled.
|
yading@7
|
97 :keyword lines_too_long: the text for each verse is too long!
|
yading@7
|
98 :keyword wrong_verses: there are some verses missing from the
|
yading@7
|
99 beginning or at the end.
|
yading@7
|
100 :keyword wrong_formatting: the text looks horrible, please fix it!
|
yading@7
|
101 """
|
yading@7
|
102 accepted = [
|
yading@7
|
103 'wrong_attribution', 'bad_characters', 'lines_too_long',
|
yading@7
|
104 'wrong_verses', 'wrong_formatting' ]
|
yading@7
|
105 if feedback in accepted:
|
yading@7
|
106 message = track.lyrics.feedback.post(
|
yading@7
|
107 track_id=self['track_id'],
|
yading@7
|
108 lyrics_id=self['track_id']['lyrics']['lyrics_id'],
|
yading@7
|
109 feedback=feedback
|
yading@7
|
110 )
|
yading@7
|
111 if not message.status_code:
|
yading@7
|
112 raise api.Error(str(message.status_code))
|
yading@7
|
113 else:
|
yading@7
|
114 raise TypeError, '%r not in %r' % (feedback, accepted)
|
yading@7
|
115
|
yading@7
|
116 class TracksCollection(ItemsCollection):
|
yading@7
|
117 """
|
yading@7
|
118 This class build a :py:class:`list` like object representing a tracks
|
yading@7
|
119 collection. It accepts :py:class:`dict` or :py:class:`Track` objects.
|
yading@7
|
120 """
|
yading@7
|
121 __allowedin__ = Track
|
yading@7
|
122
|
yading@7
|
123 @classmethod
|
yading@7
|
124 def fromAlbum(cls, **keywords):
|
yading@7
|
125 """
|
yading@7
|
126 This classmethod builds an :py:class:`TracksCollection` from a
|
yading@7
|
127 **album.tracks.get** :py:class:`musixmatch.api.Method` call.
|
yading@7
|
128
|
yading@7
|
129 :param album_id: musiXmatch album ID
|
yading@7
|
130 """
|
yading@7
|
131 return cls.fromResponseMessage(album.tracks.get(**keywords))
|
yading@7
|
132
|
yading@7
|
133 @classmethod
|
yading@7
|
134 def fromSearch(cls, **keywords):
|
yading@7
|
135 """
|
yading@7
|
136 This classmethod builds an :py:class:`TracksCollection` from a
|
yading@7
|
137 **track.search** :py:class:`musixmatch.api.Method` call.
|
yading@7
|
138
|
yading@7
|
139 :param q: a string that will be searched in every data field
|
yading@7
|
140 (q_track, q_artist, q_lyrics)
|
yading@7
|
141 :param q_track: words to be searched among track titles
|
yading@7
|
142 :param q_artist: words to be searched among artist names
|
yading@7
|
143 :param q_track_artist: words to be searched among track titles or
|
yading@7
|
144 artist names
|
yading@7
|
145 :param q_lyrics: words to be searched into the lyrics
|
yading@7
|
146 :param page: requested page of results
|
yading@7
|
147 :param page_size: desired number of items per result page
|
yading@7
|
148 :param f_has_lyrics: exclude tracks without an available lyrics
|
yading@7
|
149 (automatic if q_lyrics is set)
|
yading@7
|
150 :param f_artist_id: filter the results by the artist_id
|
yading@7
|
151 :param f_artist_mbid: filter the results by the artist_mbid
|
yading@7
|
152 :param quorum_factor: only works together with q and q_track_artist
|
yading@7
|
153 parameter. Possible values goes from 0.1 to
|
yading@7
|
154 0.9. A value of 0.9 means: 'match at least 90
|
yading@7
|
155 percent of the words'.
|
yading@7
|
156 """
|
yading@7
|
157 return cls.fromResponseMessage(track.search(**keywords))
|
yading@7
|
158
|
yading@7
|
159 @classmethod
|
yading@7
|
160 def fromChart(cls, **keywords):
|
yading@7
|
161 """
|
yading@7
|
162 This classmethod builds an :py:class:`TracksCollection` from a
|
yading@7
|
163 **track.chart.get** :py:class:`musixmatch.api.Method` call.
|
yading@7
|
164
|
yading@7
|
165 :param page: requested page of results
|
yading@7
|
166 :param page_size: desired number of items per result page
|
yading@7
|
167 :param country: the country code of the desired country chart
|
yading@7
|
168 :param f_has_lyrics: exclude tracks without an available lyrics
|
yading@7
|
169 (automatic if q_lyrics is set)
|
yading@7
|
170 """
|
yading@7
|
171 return cls.fromResponseMessage(track.chart.get(**keywords))
|