From f86a526a3740c9f7681d2b418b2a11898119ade3 Mon Sep 17 00:00:00 2001 From: Mark Greenwood Date: Sat, 10 Nov 2018 14:24:45 +0000 Subject: [PATCH 1/5] Add scrobble_filter to provide a configurable list of URI schemes not not scrobble --- README.rst | 2 ++ mopidy_scrobbler/__init__.py | 1 + mopidy_scrobbler/ext.conf | 1 + mopidy_scrobbler/frontend.py | 12 ++++++++++++ tests/test_extension.py | 2 ++ tests/test_frontend.py | 24 ++++++++++++++++++------ 6 files changed, 36 insertions(+), 6 deletions(-) diff --git a/README.rst b/README.rst index 1ce85a8..d6f4f44 100644 --- a/README.rst +++ b/README.rst @@ -41,12 +41,14 @@ found at ``~/.config/mopidy/mopidy.conf``:: [scrobbler] username = alice password = secret + scrobble_filer = list The following configuration values are available: - ``scrobbler/enabled``: If the scrobbler extension should be enabled or not. - ``scrobbler/username``: Your Last.fm username. - ``scrobbler/password``: Your Last.fm password. +- ``scrobbler/scrobble_filter``: List of uri schemes to not scrobble - eg spotify Project resources diff --git a/mopidy_scrobbler/__init__.py b/mopidy_scrobbler/__init__.py index 8785496..34f36f5 100644 --- a/mopidy_scrobbler/__init__.py +++ b/mopidy_scrobbler/__init__.py @@ -22,6 +22,7 @@ def get_config_schema(self): schema = super(Extension, self).get_config_schema() schema['username'] = config.String() schema['password'] = config.Secret() + schema['scrobble_filter'] = config.List(optional=True) return schema def setup(self, registry): diff --git a/mopidy_scrobbler/ext.conf b/mopidy_scrobbler/ext.conf index 4fded92..bb5f761 100644 --- a/mopidy_scrobbler/ext.conf +++ b/mopidy_scrobbler/ext.conf @@ -2,3 +2,4 @@ enabled = true username = password = +scrobble_filter = \ No newline at end of file diff --git a/mopidy_scrobbler/frontend.py b/mopidy_scrobbler/frontend.py index 2abcb0e..ccfbdfc 100644 --- a/mopidy_scrobbler/frontend.py +++ b/mopidy_scrobbler/frontend.py @@ -30,6 +30,14 @@ def __init__(self, config, core): self.lastfm = None self.last_start_time = None + def check_uri_scheme(self, uri): + uri_scheme = uri.split(':')[0] + if uri_scheme in self.config['scrobbler']['scrobble_filter']: + logger.info('Not scrobbling track from %s', uri_scheme) + return True + else: + return False + def on_start(self): try: self.lastfm = pylast.LastFMNetwork( @@ -43,6 +51,8 @@ def on_start(self): def track_playback_started(self, tl_track): track = tl_track.track + if self.check_uri_scheme(track.uri): + return artists = ', '.join(sorted([a.name for a in track.artists])) duration = track.length and track.length // 1000 or 0 self.last_start_time = int(time.time()) @@ -60,6 +70,8 @@ def track_playback_started(self, tl_track): def track_playback_ended(self, tl_track, time_position): track = tl_track.track + if self.check_uri_scheme(track.uri): + return artists = ', '.join(sorted([a.name for a in track.artists])) duration = track.length and track.length // 1000 or 0 time_position = time_position // 1000 diff --git a/tests/test_extension.py b/tests/test_extension.py index 756881a..5f18277 100644 --- a/tests/test_extension.py +++ b/tests/test_extension.py @@ -16,6 +16,7 @@ def test_get_default_config(self): self.assertIn('enabled = true', config) self.assertIn('username =', config) self.assertIn('password =', config) + self.assertIn('scrobble_filter', config) def test_get_config_schema(self): ext = Extension() @@ -24,6 +25,7 @@ def test_get_config_schema(self): self.assertIn('username', schema) self.assertIn('password', schema) + self.assertIn('scrobble_filter', schema) def test_setup(self): ext = Extension() diff --git a/tests/test_frontend.py b/tests/test_frontend.py index 6600ccd..94a0308 100644 --- a/tests/test_frontend.py +++ b/tests/test_frontend.py @@ -17,6 +17,7 @@ def setUp(self): 'scrobbler': { 'username': 'alice', 'password': 'secret', + 'scrobble_filter': ['spotify'] } } self.frontend = frontend_lib.ScrobblerFrontend( @@ -48,6 +49,7 @@ def test_track_playback_started_updates_now_playing(self, pylast_mock): artists = [models.Artist(name='ABC'), models.Artist(name='XYZ')] album = models.Album(name='The Collection') track = models.Track( + uri='local:track:1234567890', name='One Two Three', artists=artists, album=album, @@ -68,7 +70,7 @@ def test_track_playback_started_updates_now_playing(self, pylast_mock): def test_track_playback_started_has_default_values(self, pylast_mock): self.frontend.lastfm = mock.Mock(spec=pylast.LastFMNetwork) - track = models.Track() + track = models.Track(uri='local:track:1234567890') tl_track = models.TlTrack(track=track, tlid=17) self.frontend.track_playback_started(tl_track) @@ -86,7 +88,7 @@ def test_track_playback_started_catches_pylast_error(self, pylast_mock): pylast_mock.NetworkError = pylast.NetworkError self.frontend.lastfm.update_now_playing.side_effect = ( pylast.NetworkError(None, 'foo')) - track = models.Track() + track = models.Track(uri='local:track:1234567890') tl_track = models.TlTrack(track=track, tlid=17) self.frontend.track_playback_started(tl_track) @@ -97,6 +99,7 @@ def test_track_playback_ended_scrobbles_played_track(self, pylast_mock): artists = [models.Artist(name='ABC'), models.Artist(name='XYZ')] album = models.Album(name='The Collection') track = models.Track( + uri='local:track:1234567890', name='One Two Three', artists=artists, album=album, @@ -119,7 +122,7 @@ def test_track_playback_ended_scrobbles_played_track(self, pylast_mock): def test_track_playback_ended_has_default_values(self, pylast_mock): self.frontend.last_start_time = 123 self.frontend.lastfm = mock.Mock(spec=pylast.LastFMNetwork) - track = models.Track(length=180432) + track = models.Track(length=180432, uri='local:track:1234567890') tl_track = models.TlTrack(track=track, tlid=17) self.frontend.track_playback_ended(tl_track, 150000) @@ -135,7 +138,7 @@ def test_track_playback_ended_has_default_values(self, pylast_mock): def test_does_not_scrobble_tracks_shorter_than_30_sec(self, pylast_mock): self.frontend.lastfm = mock.Mock(spec=pylast.LastFMNetwork) - track = models.Track(length=20432) + track = models.Track(length=20432, uri='local:track:1234567890') tl_track = models.TlTrack(track=track, tlid=17) self.frontend.track_playback_ended(tl_track, 20432) @@ -144,7 +147,7 @@ def test_does_not_scrobble_tracks_shorter_than_30_sec(self, pylast_mock): def test_does_not_scrobble_if_played_less_than_half(self, pylast_mock): self.frontend.lastfm = mock.Mock(spec=pylast.LastFMNetwork) - track = models.Track(length=180432) + track = models.Track(length=180432, uri='local:track:1234567890') tl_track = models.TlTrack(track=track, tlid=17) self.frontend.track_playback_ended(tl_track, 60432) @@ -153,13 +156,22 @@ def test_does_not_scrobble_if_played_less_than_half(self, pylast_mock): def test_does_scrobble_if_played_not_half_but_240_sec(self, pylast_mock): self.frontend.lastfm = mock.Mock(spec=pylast.LastFMNetwork) - track = models.Track(length=880432) + track = models.Track(length=880432, uri='local:track:1234567890') tl_track = models.TlTrack(track=track, tlid=17) self.frontend.track_playback_ended(tl_track, 241432) self.assertEqual(self.frontend.lastfm.scrobble.call_count, 1) + def test_does_not_scrobble_if_uri_scheme_filtered(self, plylast.mock): + self.frontend.lastfm = mock.Mock(spec=pylast.LastFMNetwork) + track = models.Track(length=880432, uri='spotify:track:1234567890') + tl_track = models.TlTrack(track=track, tlid=17) + + self.frontend.track_playback_ended(tl_track, 241432) + + self.assertEqual(self.frontend.lastfm.scrobble.call_count, 0) + def test_track_playback_ended_catches_pylast_error(self, pylast_mock): self.frontend.lastfm = mock.Mock(spec=pylast.LastFMNetwork) pylast_mock.NetworkError = pylast.NetworkError From 7bb1a695e23c71492485203951c0b028c239e441 Mon Sep 17 00:00:00 2001 From: Mark Greenwood Date: Sat, 10 Nov 2018 14:29:40 +0000 Subject: [PATCH 2/5] Fix stupid typo --- tests/test_frontend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_frontend.py b/tests/test_frontend.py index 94a0308..e1620e7 100644 --- a/tests/test_frontend.py +++ b/tests/test_frontend.py @@ -163,7 +163,7 @@ def test_does_scrobble_if_played_not_half_but_240_sec(self, pylast_mock): self.assertEqual(self.frontend.lastfm.scrobble.call_count, 1) - def test_does_not_scrobble_if_uri_scheme_filtered(self, plylast.mock): + def test_does_not_scrobble_if_uri_scheme_filtered(self, pylast.mock): self.frontend.lastfm = mock.Mock(spec=pylast.LastFMNetwork) track = models.Track(length=880432, uri='spotify:track:1234567890') tl_track = models.TlTrack(track=track, tlid=17) From d95b60c9bbe71702c65d76fb5b1f478cf531e518 Mon Sep 17 00:00:00 2001 From: Mark Greenwood Date: Sat, 10 Nov 2018 14:32:44 +0000 Subject: [PATCH 3/5] Dang. More typos. --- tests/test_frontend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_frontend.py b/tests/test_frontend.py index e1620e7..5b94830 100644 --- a/tests/test_frontend.py +++ b/tests/test_frontend.py @@ -163,7 +163,7 @@ def test_does_scrobble_if_played_not_half_but_240_sec(self, pylast_mock): self.assertEqual(self.frontend.lastfm.scrobble.call_count, 1) - def test_does_not_scrobble_if_uri_scheme_filtered(self, pylast.mock): + def test_does_not_scrobble_if_uri_scheme_filtered(self, pylast_mock): self.frontend.lastfm = mock.Mock(spec=pylast.LastFMNetwork) track = models.Track(length=880432, uri='spotify:track:1234567890') tl_track = models.TlTrack(track=track, tlid=17) From f15eda87466845cf5ccb633a64c9308058096ef4 Mon Sep 17 00:00:00 2001 From: Mark Greenwood Date: Sun, 11 Nov 2018 21:25:00 +0000 Subject: [PATCH 4/5] Makes artists concatenate like RompR does it --- mopidy_scrobbler/frontend.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/mopidy_scrobbler/frontend.py b/mopidy_scrobbler/frontend.py index ccfbdfc..ad0d180 100644 --- a/mopidy_scrobbler/frontend.py +++ b/mopidy_scrobbler/frontend.py @@ -38,6 +38,16 @@ def check_uri_scheme(self, uri): else: return False + def concatenate_artist_names(self, objects): + names = sorted([a.name for a in objects]); + if len(names) == 0: + return '' + elif len(names) < 3: + return ' & '.join(names) + else: + first_lot = names[:len(names)-1] + return ', '.join(first_lot) + ' & ' + names[len(names)-1] + def on_start(self): try: self.lastfm = pylast.LastFMNetwork( @@ -53,7 +63,9 @@ def track_playback_started(self, tl_track): track = tl_track.track if self.check_uri_scheme(track.uri): return - artists = ', '.join(sorted([a.name for a in track.artists])) + artists = self.concatenate_artist_names(track.artists) + albumartists = self.concatenate_artist_names(track.album.artists) + logger.info("Scrobbler : Album Artists are %s", albumartists) duration = track.length and track.length // 1000 or 0 self.last_start_time = int(time.time()) logger.debug('Now playing track: %s - %s', artists, track.name) @@ -62,6 +74,7 @@ def track_playback_started(self, tl_track): artists, (track.name or ''), album=(track.album and track.album.name or ''), + album_artist=albumartists, duration=str(duration), track_number=str(track.track_no or 0), mbid=(track.musicbrainz_id or '')) @@ -72,7 +85,8 @@ def track_playback_ended(self, tl_track, time_position): track = tl_track.track if self.check_uri_scheme(track.uri): return - artists = ', '.join(sorted([a.name for a in track.artists])) + artists = self.concatenate_artist_names(track.artists) + albumartists = self.concatenate_artist_names(track.album.artists) duration = track.length and track.length // 1000 or 0 time_position = time_position // 1000 if duration < 30: @@ -91,6 +105,7 @@ def track_playback_ended(self, tl_track, time_position): (track.name or ''), str(self.last_start_time), album=(track.album and track.album.name or ''), + album_artist=albumartists, track_number=str(track.track_no or 0), duration=str(duration), mbid=(track.musicbrainz_id or '')) From 0273775125093155e2e3bc74736996edbc84847e Mon Sep 17 00:00:00 2001 From: Mark Greenwood Date: Sun, 11 Nov 2018 21:36:17 +0000 Subject: [PATCH 5/5] Not sure what happened there, git mixed up two branches. This should be correct. --- README.rst | 4 ++-- mopidy_scrobbler/__init__.py | 2 +- mopidy_scrobbler/ext.conf | 2 +- mopidy_scrobbler/frontend.py | 21 +++------------------ tests/test_extension.py | 4 ++-- tests/test_frontend.py | 2 +- 6 files changed, 10 insertions(+), 25 deletions(-) diff --git a/README.rst b/README.rst index d6f4f44..792ddbc 100644 --- a/README.rst +++ b/README.rst @@ -41,14 +41,14 @@ found at ``~/.config/mopidy/mopidy.conf``:: [scrobbler] username = alice password = secret - scrobble_filer = list + backend_blacklist = list The following configuration values are available: - ``scrobbler/enabled``: If the scrobbler extension should be enabled or not. - ``scrobbler/username``: Your Last.fm username. - ``scrobbler/password``: Your Last.fm password. -- ``scrobbler/scrobble_filter``: List of uri schemes to not scrobble - eg spotify +- ``scrobbler/backend_blacklist``: List of uri schemes to not scrobble - eg spotify Project resources diff --git a/mopidy_scrobbler/__init__.py b/mopidy_scrobbler/__init__.py index 34f36f5..2d03828 100644 --- a/mopidy_scrobbler/__init__.py +++ b/mopidy_scrobbler/__init__.py @@ -22,7 +22,7 @@ def get_config_schema(self): schema = super(Extension, self).get_config_schema() schema['username'] = config.String() schema['password'] = config.Secret() - schema['scrobble_filter'] = config.List(optional=True) + schema['backend_blacklist'] = config.List(optional=True) return schema def setup(self, registry): diff --git a/mopidy_scrobbler/ext.conf b/mopidy_scrobbler/ext.conf index bb5f761..706ff70 100644 --- a/mopidy_scrobbler/ext.conf +++ b/mopidy_scrobbler/ext.conf @@ -2,4 +2,4 @@ enabled = true username = password = -scrobble_filter = \ No newline at end of file +backend_blacklist = \ No newline at end of file diff --git a/mopidy_scrobbler/frontend.py b/mopidy_scrobbler/frontend.py index ad0d180..34ada07 100644 --- a/mopidy_scrobbler/frontend.py +++ b/mopidy_scrobbler/frontend.py @@ -32,22 +32,12 @@ def __init__(self, config, core): def check_uri_scheme(self, uri): uri_scheme = uri.split(':')[0] - if uri_scheme in self.config['scrobbler']['scrobble_filter']: + if uri_scheme in self.config['scrobbler']['backend_blacklist']: logger.info('Not scrobbling track from %s', uri_scheme) return True else: return False - def concatenate_artist_names(self, objects): - names = sorted([a.name for a in objects]); - if len(names) == 0: - return '' - elif len(names) < 3: - return ' & '.join(names) - else: - first_lot = names[:len(names)-1] - return ', '.join(first_lot) + ' & ' + names[len(names)-1] - def on_start(self): try: self.lastfm = pylast.LastFMNetwork( @@ -63,9 +53,7 @@ def track_playback_started(self, tl_track): track = tl_track.track if self.check_uri_scheme(track.uri): return - artists = self.concatenate_artist_names(track.artists) - albumartists = self.concatenate_artist_names(track.album.artists) - logger.info("Scrobbler : Album Artists are %s", albumartists) + artists = ', '.join(sorted([a.name for a in track.artists])) duration = track.length and track.length // 1000 or 0 self.last_start_time = int(time.time()) logger.debug('Now playing track: %s - %s', artists, track.name) @@ -74,7 +62,6 @@ def track_playback_started(self, tl_track): artists, (track.name or ''), album=(track.album and track.album.name or ''), - album_artist=albumartists, duration=str(duration), track_number=str(track.track_no or 0), mbid=(track.musicbrainz_id or '')) @@ -85,8 +72,7 @@ def track_playback_ended(self, tl_track, time_position): track = tl_track.track if self.check_uri_scheme(track.uri): return - artists = self.concatenate_artist_names(track.artists) - albumartists = self.concatenate_artist_names(track.album.artists) + artists = ', '.join(sorted([a.name for a in track.artists])) duration = track.length and track.length // 1000 or 0 time_position = time_position // 1000 if duration < 30: @@ -105,7 +91,6 @@ def track_playback_ended(self, tl_track, time_position): (track.name or ''), str(self.last_start_time), album=(track.album and track.album.name or ''), - album_artist=albumartists, track_number=str(track.track_no or 0), duration=str(duration), mbid=(track.musicbrainz_id or '')) diff --git a/tests/test_extension.py b/tests/test_extension.py index 5f18277..ff610e3 100644 --- a/tests/test_extension.py +++ b/tests/test_extension.py @@ -16,7 +16,7 @@ def test_get_default_config(self): self.assertIn('enabled = true', config) self.assertIn('username =', config) self.assertIn('password =', config) - self.assertIn('scrobble_filter', config) + self.assertIn('backend_blacklist', config) def test_get_config_schema(self): ext = Extension() @@ -25,7 +25,7 @@ def test_get_config_schema(self): self.assertIn('username', schema) self.assertIn('password', schema) - self.assertIn('scrobble_filter', schema) + self.assertIn('backend_blacklist', schema) def test_setup(self): ext = Extension() diff --git a/tests/test_frontend.py b/tests/test_frontend.py index 5b94830..3b6e0a9 100644 --- a/tests/test_frontend.py +++ b/tests/test_frontend.py @@ -17,7 +17,7 @@ def setUp(self): 'scrobbler': { 'username': 'alice', 'password': 'secret', - 'scrobble_filter': ['spotify'] + 'backend_blacklist': ['spotify'] } } self.frontend = frontend_lib.ScrobblerFrontend(