yading@7: """ yading@7: This module contains higher level classes to query Musixmatch API and build yading@7: simple dictionary-like objects representing a Track or a TracksCollection. yading@7: yading@7: >>> from musixmatch.track import Track, TracksCollection yading@7: >>> import musixmatch.api yading@7: >>> yading@7: >>> try: yading@7: ... track = Track(track_mbid=8976) yading@7: ... collection = TracksCollection.fromChart(country='us', page=1) yading@7: ... except musixmatch.api.Error, e: yading@7: ... pass yading@7: """ yading@7: import musixmatch yading@7: __license__ = musixmatch.__license__ yading@7: __author__ = musixmatch.__author__ yading@7: yading@7: from musixmatch import api, lyrics, subtitle yading@7: from musixmatch.base import Item, ItemsCollection yading@7: from musixmatch.ws import track, matcher, album yading@7: yading@7: _marker=object() yading@7: yading@7: class Track(Item): yading@7: """ yading@7: This class builds a :py:class:`dict` like object representing a track. It yading@7: can get track information through the :py:class:`musixmatch.api.Method` yading@7: **track.get** or from an already well-formed :py:class:`dict`. Create a yading@7: Track object based on a given keyword argument: yading@7: yading@7: :param track_id: musiXmatch track ID yading@7: :param musicbrainz_id: Musicbrainz track ID yading@7: :param track_echonest_id: Echonest track ID yading@7: :param track_data: an already well-formed :py:class:`dict` of track data yading@7: :raises: :py:exc:`musixmatch.api.Error` if :py:class:`musixmatch.api.ResponseStatusCode` is not 200 yading@7: yading@7: Once information are collected, the following keys are available: yading@7: yading@7: :keyword track_id: musiXmatch track ID yading@7: :keyword track_mbid: Musicbrainz track ID yading@7: :keyword lyrics_id: musiXmatch lyrics ID yading@7: :keyword instrumental: wether the track is instrumental or not yading@7: :keyword subtitle_id: musiXmatch subtitle ID yading@7: :keyword track_name: track name yading@7: :keyword album_coverart_100x100: album cover URL yading@7: :keyword artist_id: musiXmatch artist ID yading@7: :keyword artist_mbid: Musicbrainz artist ID yading@7: :keyword artist_name: artist name yading@7: yading@7: Keyword access have been overloaded thanks to the :py:meth:`get` method yading@7: which will eventually fetch the matching lyrics or subtitle. yading@7: """ yading@7: __api_method__ = track.get yading@7: yading@7: @classmethod yading@7: def fromMatcher(cls, **keywords): yading@7: """ yading@7: Returns a :py:class:`Track` based on the result of the yading@7: :py:class:`musiXmatch.api.Method` **matcher.track.get**. Accepts the yading@7: following keywords: yading@7: yading@7: :param q_track: words to be searched among track titles yading@7: :param q_artist: words to be searched among artist names yading@7: """ yading@7: return cls.fromResponseMessage(matcher.track.get(**keywords)) yading@7: yading@7: def get(self, key, default=_marker): yading@7: """ yading@7: If key is *lyrics* or *subtitle* try to query api for proper value, yading@7: and build an :py:class:`musixmatch.lyrics.Lyrics` or yading@7: :py:class:`musixmatch.subtitle.Subtitle`. Access to the above mentioned yading@7: keys may fail with :py:exc:`musixmatch.api.Error`. Once fetched, the yading@7: result is saved. yading@7: """ yading@7: special = { yading@7: 'lyrics': lyrics.Lyrics, yading@7: 'subtitle': subtitle.Subtitle, yading@7: } yading@7: if key in special and not key in self: yading@7: self[key] = special[key](track_id=self['track_id']) yading@7: value = dict.get(self, key, default) yading@7: if value == _marker: yading@7: raise KeyError, key yading@7: return value yading@7: yading@7: def __getitem__(self, key): yading@7: return self.get(key) yading@7: yading@7: def postFeedback(self, feedback): yading@7: """ yading@7: Post feedback about lyrics for this track. **feedback** can be one of: yading@7: yading@7: :keyword wrong_attribution: the lyrics shown are not by the artist that yading@7: I selected. yading@7: :keyword bad_characters: there are strange characters and/or words yading@7: that are partially scrambled. yading@7: :keyword lines_too_long: the text for each verse is too long! yading@7: :keyword wrong_verses: there are some verses missing from the yading@7: beginning or at the end. yading@7: :keyword wrong_formatting: the text looks horrible, please fix it! yading@7: """ yading@7: accepted = [ yading@7: 'wrong_attribution', 'bad_characters', 'lines_too_long', yading@7: 'wrong_verses', 'wrong_formatting' ] yading@7: if feedback in accepted: yading@7: message = track.lyrics.feedback.post( yading@7: track_id=self['track_id'], yading@7: lyrics_id=self['track_id']['lyrics']['lyrics_id'], yading@7: feedback=feedback yading@7: ) yading@7: if not message.status_code: yading@7: raise api.Error(str(message.status_code)) yading@7: else: yading@7: raise TypeError, '%r not in %r' % (feedback, accepted) yading@7: yading@7: class TracksCollection(ItemsCollection): yading@7: """ yading@7: This class build a :py:class:`list` like object representing a tracks yading@7: collection. It accepts :py:class:`dict` or :py:class:`Track` objects. yading@7: """ yading@7: __allowedin__ = Track yading@7: yading@7: @classmethod yading@7: def fromAlbum(cls, **keywords): yading@7: """ yading@7: This classmethod builds an :py:class:`TracksCollection` from a yading@7: **album.tracks.get** :py:class:`musixmatch.api.Method` call. yading@7: yading@7: :param album_id: musiXmatch album ID yading@7: """ yading@7: return cls.fromResponseMessage(album.tracks.get(**keywords)) yading@7: yading@7: @classmethod yading@7: def fromSearch(cls, **keywords): yading@7: """ yading@7: This classmethod builds an :py:class:`TracksCollection` from a yading@7: **track.search** :py:class:`musixmatch.api.Method` call. yading@7: yading@7: :param q: a string that will be searched in every data field yading@7: (q_track, q_artist, q_lyrics) yading@7: :param q_track: words to be searched among track titles yading@7: :param q_artist: words to be searched among artist names yading@7: :param q_track_artist: words to be searched among track titles or yading@7: artist names yading@7: :param q_lyrics: words to be searched into the lyrics yading@7: :param page: requested page of results yading@7: :param page_size: desired number of items per result page yading@7: :param f_has_lyrics: exclude tracks without an available lyrics yading@7: (automatic if q_lyrics is set) yading@7: :param f_artist_id: filter the results by the artist_id yading@7: :param f_artist_mbid: filter the results by the artist_mbid yading@7: :param quorum_factor: only works together with q and q_track_artist yading@7: parameter. Possible values goes from 0.1 to yading@7: 0.9. A value of 0.9 means: 'match at least 90 yading@7: percent of the words'. yading@7: """ yading@7: return cls.fromResponseMessage(track.search(**keywords)) yading@7: yading@7: @classmethod yading@7: def fromChart(cls, **keywords): yading@7: """ yading@7: This classmethod builds an :py:class:`TracksCollection` from a yading@7: **track.chart.get** :py:class:`musixmatch.api.Method` call. yading@7: yading@7: :param page: requested page of results yading@7: :param page_size: desired number of items per result page yading@7: :param country: the country code of the desired country chart yading@7: :param f_has_lyrics: exclude tracks without an available lyrics yading@7: (automatic if q_lyrics is set) yading@7: """ yading@7: return cls.fromResponseMessage(track.chart.get(**keywords))