diff --git a/pylast.py b/pylast.py index baeac57..7b66644 100644 --- a/pylast.py +++ b/pylast.py @@ -122,10 +122,10 @@ RE_XML_ILLEGAL = (u'([\u0000-\u0008\u000b-\u000c\u000e-\u001f\ufffe-\uffff])' + u'|' + u'([%s-%s][^%s-%s])|([^%s-%s][%s-%s])|([%s-%s]$)|(^[%s-%s])' % - (unichr(0xd800), unichr(0xdbff), unichr(0xdc00), - unichr(0xdfff), unichr(0xd800), unichr(0xdbff), - unichr(0xdc00), unichr(0xdfff), unichr(0xd800), - unichr(0xdbff), unichr(0xdc00), unichr(0xdfff))) + (unichr(0xd800), unichr(0xdbff), unichr(0xdc00), + unichr(0xdfff), unichr(0xd800), unichr(0xdbff), + unichr(0xdc00), unichr(0xdfff), unichr(0xd800), + unichr(0xdbff), unichr(0xdc00), unichr(0xdfff))) XML_ILLEGAL = re.compile(RE_XML_ILLEGAL) @@ -426,12 +426,18 @@ class _Network(object): params = {} - if long: params["long"] = long - if lat: params["lat"] = lat - if location: params["location"] = location - if limit: params["limit"] = limit - if distance: params["distance"] = distance - if tag: params["tag"] = tag + if long: + params["long"] = long + if lat: + params["lat"] = lat + if location: + params["location"] = location + if limit: + params["limit"] = limit + if distance: + params["distance"] = distance + if tag: + params["tag"] = tag if festivalsonly: params["festivalsonly"] = 1 elif not festivalsonly: @@ -672,12 +678,18 @@ class _Network(object): params = {"track": title, "artist": artist} - if album: params["album"] = album - if album_artist: params["albumArtist"] = album_artist - if context: params["context"] = context - if track_number: params["trackNumber"] = track_number - if mbid: params["mbid"] = mbid - if duration: params["duration"] = duration + if album: + params["album"] = album + if album_artist: + params["albumArtist"] = album_artist + if context: + params["context"] = context + if track_number: + params["trackNumber"] = track_number + if mbid: + params["mbid"] = mbid + if duration: + params["duration"] = duration _Request(self, "track.updateNowPlaying", params).execute() @@ -1024,7 +1036,7 @@ class _Request(object): def sign_it(self): """Sign this request.""" - if not "api_sig" in self.params.keys(): + if "api_sig" not in self.params.keys(): self.params['api_sig'] = self._get_signature() def _get_signature(self): @@ -2091,6 +2103,17 @@ class Artist(_BaseObject, _Taggable): self._request("artist.Shout", False, params) + def get_band_members(self): + """Returns a list of band members or None if unknown.""" + + names = None + doc = self._request(self.ws_prefix + ".getInfo", True) + + for node in doc.getElementsByTagName("bandmembers"): + names = _extract_all(node, "name") + + return names + class Event(_BaseObject): """An event.""" @@ -3367,12 +3390,22 @@ class User(_BaseObject, _Chartable): return Track(artist, title, self.network, self.name) - def get_recent_tracks(self, limit=10, cacheable=True): + def get_recent_tracks(self, limit=10, cacheable=True, + time_from=None, time_to=None): """ Returns this user's played track as a sequence of PlayedTrack objects in reverse order of playtime, all the way back to the first track. - If limit==None, it will try to pull all the available data. + Parameters: + limit : If None, it will try to pull all the available data. + from (Optional) : Beginning timestamp of a range - only display + scrobbles after this time, in UNIX timestamp format (integer + number of seconds since 00:00:00, January 1st 1970 UTC). This + must be in the UTC time zone. + to (Optional) : End timestamp of a range - only display scrobbles + before this time, in UNIX timestamp format (integer number of + seconds since 00:00:00, January 1st 1970 UTC). This must be in + the UTC time zone. This method uses caching. Enable caching only if you're pulling a large amount of data. @@ -3384,6 +3417,10 @@ class User(_BaseObject, _Chartable): params = self._get_params() if limit: params['limit'] = limit + if time_from: + params['from'] = time_from + if time_to: + params['to'] = time_to seq = [] for track in _collect_nodes( @@ -4116,7 +4153,7 @@ def _number(string): def _unescape_htmlentity(string): - #string = _unicode(string) + # string = _unicode(string) mapping = htmlentitydefs.name2codepoint for key in mapping: diff --git a/test_pylast.py b/test_pylast.py index 0bd0f0b..52244bf 100755 --- a/test_pylast.py +++ b/test_pylast.py @@ -1251,7 +1251,7 @@ class TestPyLast(unittest.TestCase): # Assert # Check inbox for spam! - #album/artist/event/track/user + # album/artist/event/track/user def test_album_shouts(self): # Arrange @@ -1750,7 +1750,6 @@ class TestPyLast(unittest.TestCase): self.assertEqual(mbid, "a74b1b7f-71a5-4011-9441-d0b5e4122711") self.assertIsInstance(streamable, bool) - def test_events(self): # Arrange event_id_1 = 3162700 # Glasto 2013 @@ -1869,6 +1868,46 @@ class TestPyLast(unittest.TestCase): # Act / Assert self.assertTrue(event1 != event2) + def test_band_members(self): + # Arrange + artist = pylast.Artist("The Beatles", self.network) + + # Act + band_members = artist.get_band_members() + + # Assert + self.assertGreaterEqual(len(band_members), 4) + + def test_no_band_members(self): + # Arrange + artist = pylast.Artist("John Lennon", self.network) + + # Act + band_members = artist.get_band_members() + + # Assert + self.assertIsNone(band_members) + + def test_get_recent_tracks_from_to(self): + # Arrange + lastfm_user = self.network.get_user("RJ") + + from datetime import datetime + start = datetime(2011, 7, 21, 15, 10) + end = datetime(2011, 7, 21, 15, 15) + import calendar + utc_start = calendar.timegm(start.utctimetuple()) + utc_end = calendar.timegm(end.utctimetuple()) + + # Act + tracks = lastfm_user.get_recent_tracks(time_from=utc_start, + time_to=utc_end) + + # Assert + self.assertEqual(len(tracks), 1) + self.assertEqual(str(tracks[0].track.artist), "Johnny Cash") + self.assertEqual(str(tracks[0].track.title), "Ring of Fire") + if __name__ == '__main__': parser = argparse.ArgumentParser(