From 9d125506e5c28a7227c74f37dec0ca5575c78a67 Mon Sep 17 00:00:00 2001 From: Hugo Date: Tue, 26 Feb 2019 22:53:31 +0200 Subject: [PATCH 1/5] Deprecate User.get_artist_tracks as Last.fm will remove it soon --- CHANGELOG.md | 5 +++++ pylast/__init__.py | 12 ++++++++++-- tests/test_album.py | 5 ++++- tests/test_user.py | 9 ++++++--- 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f37251..a36d432 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Extract username from session via new `SessionKeyGenerator.get_web_auth_session_key_username` ([#290]) +### Deprecated + +* `User.get_artist_tracks` ([#298]) + ## [3.0.0] - 2019-01-01 ### Added * This changelog file ([#273]) @@ -31,6 +35,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [Unreleased]: https://github.com/pylast/pylast/compare/v3.0.0...HEAD [3.0.0]: https://github.com/pylast/pylast/compare/2.4.0...3.0.0 [2.4.0]: https://github.com/pylast/pylast/compare/2.3.0...2.4.0 +[#298]: https://github.com/pylast/pylast/issues/298 [#290]: https://github.com/pylast/pylast/pull/290 [#265]: https://github.com/pylast/pylast/issues/265 [#273]: https://github.com/pylast/pylast/issues/273 diff --git a/pylast/__init__.py b/pylast/__init__.py index 5b839c9..1cd1a2e 100644 --- a/pylast/__init__.py +++ b/pylast/__init__.py @@ -20,18 +20,19 @@ # # https://github.com/pylast/pylast -from xml.dom import minidom, Node import collections import hashlib +import html.entities import logging import shelve import ssl import sys import tempfile import time +import warnings import xml.dom -import html.entities from http.client import HTTPSConnection +from xml.dom import Node, minidom from . import version @@ -2236,6 +2237,13 @@ class User(_BaseObject, _Chartable): # Not implemented: # "Can be limited to specific timeranges, defaults to all time." + warnings.warn( + "User.get_artist_tracks is deprecated and will be removed in a future " + "version: https://github.com/pylast/pylast/issues/298", + DeprecationWarning, + stacklevel=2, + ) + params = self._get_params() params["artist"] = artist diff --git a/tests/test_album.py b/tests/test_album.py index a2836fd..c855d48 100755 --- a/tests/test_album.py +++ b/tests/test_album.py @@ -3,6 +3,7 @@ Integration (not unit) tests for pylast.py """ import unittest +import warnings import pylast @@ -44,7 +45,9 @@ class TestPyLastAlbum(TestPyLastWithLastFm): lastfm_user = self.network.get_user(self.username) # Act - track = lastfm_user.get_artist_tracks(artist="Test Artist")[0] + with warnings.catch_warnings(): + warnings.filterwarnings("ignore", category=DeprecationWarning) + track = lastfm_user.get_artist_tracks(artist="Test Artist")[0] # Assert self.assertTrue(hasattr(track, "album")) diff --git a/tests/test_user.py b/tests/test_user.py index 1e7c76d..bd019ea 100755 --- a/tests/test_user.py +++ b/tests/test_user.py @@ -4,6 +4,7 @@ Integration (not unit) tests for pylast.py """ import os import unittest +import warnings import pylast @@ -186,9 +187,11 @@ class TestPyLastUser(TestPyLastWithLastFm): lastfm_user = self.network.get_authenticated_user() # Act - result1 = lastfm_user.get_artist_tracks("Test Artist", cacheable=False) - result2 = lastfm_user.get_artist_tracks("Test Artist", cacheable=True) - result3 = lastfm_user.get_artist_tracks("Test Artist") + with warnings.catch_warnings(): + warnings.filterwarnings("ignore", category=DeprecationWarning) + result1 = lastfm_user.get_artist_tracks("Test Artist", cacheable=False) + result2 = lastfm_user.get_artist_tracks("Test Artist", cacheable=True) + result3 = lastfm_user.get_artist_tracks("Test Artist") # Assert self.helper_validate_results(result1, result2, result3) From 65e182c5ddfad8b3e097ed75ff92a8c83fcd883e Mon Sep 17 00:00:00 2001 From: Hugo Date: Wed, 27 Feb 2019 17:01:23 +0200 Subject: [PATCH 2/5] Aside: make test_scrobble more reliable --- tests/test_network.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/test_network.py b/tests/test_network.py index 448e535..36188ec 100755 --- a/tests/test_network.py +++ b/tests/test_network.py @@ -20,15 +20,14 @@ class TestPyLastNetwork(TestPyLastWithLastFm): lastfm_user = self.network.get_user(self.username) # Act + self.network.scrobble(artist=artist, title="test title 2", timestamp=timestamp) self.network.scrobble(artist=artist, title=title, timestamp=timestamp) - time.sleep(1) # Delay, for Last.fm latency. TODO Can this be removed later? # Assert # limit=2 to ignore now-playing: last_scrobble = lastfm_user.get_recent_tracks(limit=2)[0] self.assertEqual(str(last_scrobble.track.artist).lower(), artist) self.assertEqual(str(last_scrobble.track.title).lower(), title) - self.assertEqual(str(last_scrobble.timestamp), str(timestamp)) def test_update_now_playing(self): # Arrange From dc464788f22a3cbf79330c206f6ac6e26700b11b Mon Sep 17 00:00:00 2001 From: Hugo Date: Wed, 27 Feb 2019 17:19:20 +0200 Subject: [PATCH 3/5] Aside: make test_album_tags_are_topitems more reliable --- tests/test_album.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_album.py b/tests/test_album.py index c855d48..2d47720 100755 --- a/tests/test_album.py +++ b/tests/test_album.py @@ -13,10 +13,10 @@ from .test_pylast import TestPyLastWithLastFm class TestPyLastAlbum(TestPyLastWithLastFm): def test_album_tags_are_topitems(self): # Arrange - albums = self.network.get_user("RJ").get_top_albums() + album = self.network.get_album("Test Artist", "Test Album") # Act - tags = albums[0].item.get_top_tags(limit=1) + tags = album.get_top_tags(limit=1) # Assert self.assertGreater(len(tags), 0) From da473448f48ce1cb7a9b1aae95ea66e84e1c3337 Mon Sep 17 00:00:00 2001 From: Hugo Date: Wed, 27 Feb 2019 19:47:31 +0200 Subject: [PATCH 4/5] Add support for new user.getTrackScrobbles --- pylast/__init__.py | 26 ++++++++++++++++++++++++++ tests/test_user.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/pylast/__init__.py b/pylast/__init__.py index 1cd1a2e..3d1e4ab 100644 --- a/pylast/__init__.py +++ b/pylast/__init__.py @@ -2533,6 +2533,32 @@ class User(_BaseObject, _Chartable): return self._get_things("getTopTracks", "track", Track, params, cacheable) + def get_track_scrobbles(self, artist, track, cacheable=False): + """ + Get a list of scrobbles of this tracks by this artist scrobbled by this user, + including scrobble time. + """ + + params = self._get_params() + params["artist"] = artist + params["track"] = track + + seq = [] + for track in _collect_nodes( + None, self, self.ws_prefix + ".getTrackScrobbles", cacheable, params + ): + title = _extract(track, "name") + artist = _extract(track, "artist") + date = _extract(track, "date") + album = _extract(track, "album") + timestamp = track.getElementsByTagName("date")[0].getAttribute("uts") + + seq.append( + PlayedTrack(Track(artist, title, self.network), album, date, timestamp) + ) + + return seq + def get_image(self, size=SIZE_EXTRA_LARGE): """ Returns the user's avatar diff --git a/tests/test_user.py b/tests/test_user.py index bd019ea..cf37c09 100755 --- a/tests/test_user.py +++ b/tests/test_user.py @@ -433,6 +433,34 @@ class TestPyLastUser(TestPyLastWithLastFm): self.assertIsNotNone(track) self.assertIsInstance(track.network, pylast.LastFMNetwork) + def test_user_get_track_scrobbles(self): + # Arrange + artist = "France Gall" + title = "Laisse Tomber Les Filles" + user = self.network.get_user("bbc6music") + + # Act + scrobbles = user.get_track_scrobbles(artist, title) + + # Assert + self.assertGreater(len(scrobbles), 0) + self.assertEqual(str(scrobbles[0].track.artist), "France Gall") + self.assertEqual(scrobbles[0].track.title, "Laisse Tomber Les Filles") + + def test_cacheable_user_get_track_scrobbles(self): + # Arrange + artist = "France Gall" + title = "Laisse Tomber Les Filles" + user = self.network.get_user("bbc6music") + + # Act + result1 = user.get_track_scrobbles(artist, title, cacheable=False) + result2 = user.get_track_scrobbles(artist, title, cacheable=True) + result3 = user.get_track_scrobbles(artist, title) + + # Assert + self.helper_validate_results(result1, result2, result3) + if __name__ == "__main__": unittest.main(failfast=True) From 2284dcf5801e9576aea3e7300adacb86705aa3a8 Mon Sep 17 00:00:00 2001 From: Hugo Date: Wed, 27 Feb 2019 19:56:29 +0200 Subject: [PATCH 5/5] Update docs --- CHANGELOG.md | 4 +++- pylast/__init__.py | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a36d432..8c7eb76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,10 +10,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Extract username from session via new `SessionKeyGenerator.get_web_auth_session_key_username` ([#290]) +* `User.get_track_scrobbles` ([#298]) ### Deprecated -* `User.get_artist_tracks` ([#298]) +* `User.get_artist_tracks`. Use `User.get_track_scrobbles` as a partial replacement. + ([#298]) ## [3.0.0] - 2019-01-01 ### Added diff --git a/pylast/__init__.py b/pylast/__init__.py index 3d1e4ab..b0c2fda 100644 --- a/pylast/__init__.py +++ b/pylast/__init__.py @@ -2239,7 +2239,8 @@ class User(_BaseObject, _Chartable): warnings.warn( "User.get_artist_tracks is deprecated and will be removed in a future " - "version: https://github.com/pylast/pylast/issues/298", + "version. User.get_track_scrobbles is a partial replacement. " + "See https://github.com/pylast/pylast/issues/298", DeprecationWarning, stacklevel=2, ) @@ -2535,7 +2536,7 @@ class User(_BaseObject, _Chartable): def get_track_scrobbles(self, artist, track, cacheable=False): """ - Get a list of scrobbles of this tracks by this artist scrobbled by this user, + Get a list of this user's scrobbles of this artist's track, including scrobble time. """