diff --git a/docs/api/frontends.rst b/docs/api/frontends.rst index 6da5d337..96d8266e 100644 --- a/docs/api/frontends.rst +++ b/docs/api/frontends.rst @@ -50,4 +50,3 @@ Frontend implementations * :mod:`mopidy.frontends.http` * :mod:`mopidy.frontends.mpd` * :mod:`mopidy.frontends.mpris` -* :mod:`mopidy.frontends.scrobbler` diff --git a/docs/changelog.rst b/docs/changelog.rst index b37b24c5..244ed592 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -7,6 +7,13 @@ This changelog is used to track all major changes to Mopidy. v0.16.0 (UNRELEASED) ==================== +**Extensions** + +- The Last.fm scrobbler has been moved to its own external extension, + `Mopidy-Scrobbler `. You'll need + to install it in addition to Mopidy if you want it to continue to work as it + used to. + **Audio** - Added support for parsing and playback of playlists in GStreamer. What this diff --git a/docs/ext/index.rst b/docs/ext/index.rst index 736f2fb6..f3d3f7c2 100644 --- a/docs/ext/index.rst +++ b/docs/ext/index.rst @@ -77,6 +77,21 @@ Issues: https://github.com/mopidy/mopidy/issues +Mopidy-Scrobbler +---------------- + +Extension for scrobbling played tracks to Last.fm. + +Author: + Stein Magnus Jodal +PyPI: + `Mopidy-Scrobbler `_ +GitHub: + `mopidy/mopidy-scrobbler `_ +Issues: + https://github.com/mopidy/mopidy-scrobbler/issues + + Mopidy-SomaFM ------------- diff --git a/docs/ext/scrobbler.rst b/docs/ext/scrobbler.rst deleted file mode 100644 index 84188d02..00000000 --- a/docs/ext/scrobbler.rst +++ /dev/null @@ -1,55 +0,0 @@ -.. _ext-scrobbler: - -**************** -Mopidy-Scrobbler -**************** - -This extension scrobbles the music you play to your `Last.fm -`_ profile. - -.. note:: - - This extension requires a free user account at Last.fm. - - -Dependencies -============ - -.. literalinclude:: ../../requirements/scrobbler.txt - - -Default configuration -===================== - -.. literalinclude:: ../../mopidy/frontends/scrobbler/ext.conf - :language: ini - - -Configuration values -==================== - -.. confval:: scrobbler/enabled - - If the scrobbler extension should be enabled or not. - -.. confval:: scrobbler/username - - Your Last.fm username. - -.. confval:: scrobbler/password - - Your Last.fm password. - - -Usage -===== - -The extension is enabled by default if all dependencies are available. You just -need to add your Last.fm username and password to the -``~/.config/mopidy/mopidy.conf`` file: - -.. code-block:: ini - - [scrobbler] - username = myusername - password = mysecret diff --git a/docs/glossary.rst b/docs/glossary.rst index 2aa63887..102af3b6 100644 --- a/docs/glossary.rst +++ b/docs/glossary.rst @@ -25,9 +25,9 @@ Glossary frontend A part of Mopidy *using* the :term:`core` API. Existing frontends include the :ref:`MPD server `, the :ref:`MPRIS/D-Bus - integration `, the :ref:`Last.fm scrobbler `, - and the :ref:`HTTP server ` with JavaScript API. See - :ref:`frontend-api` for details. + integration `, the Last.fm scrobbler, and the :ref:`HTTP + server ` with JavaScript API. See :ref:`frontend-api` for + details. mixer A GStreamer element that controls audio volume. diff --git a/mopidy/frontends/scrobbler/__init__.py b/mopidy/frontends/scrobbler/__init__.py deleted file mode 100644 index c08bc15e..00000000 --- a/mopidy/frontends/scrobbler/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -from __future__ import unicode_literals - -import os - -import mopidy -from mopidy import config, exceptions, ext - - -class Extension(ext.Extension): - - dist_name = 'Mopidy-Scrobbler' - ext_name = 'scrobbler' - version = mopidy.__version__ - - def get_default_config(self): - conf_file = os.path.join(os.path.dirname(__file__), 'ext.conf') - return config.read(conf_file) - - def get_config_schema(self): - schema = super(Extension, self).get_config_schema() - schema['username'] = config.String() - schema['password'] = config.Secret() - return schema - - def validate_environment(self): - try: - import pylast # noqa - except ImportError as e: - raise exceptions.ExtensionError('pylast library not found', e) - - def get_frontend_classes(self): - from .actor import ScrobblerFrontend - return [ScrobblerFrontend] diff --git a/mopidy/frontends/scrobbler/actor.py b/mopidy/frontends/scrobbler/actor.py deleted file mode 100644 index 2343e0cb..00000000 --- a/mopidy/frontends/scrobbler/actor.py +++ /dev/null @@ -1,81 +0,0 @@ -from __future__ import unicode_literals - -import logging -import time - -import pykka -import pylast - -from mopidy.core import CoreListener - - -logger = logging.getLogger('mopidy.frontends.scrobbler') - -API_KEY = '2236babefa8ebb3d93ea467560d00d04' -API_SECRET = '94d9a09c0cd5be955c4afaeaffcaefcd' - - -class ScrobblerFrontend(pykka.ThreadingActor, CoreListener): - def __init__(self, config, core): - super(ScrobblerFrontend, self).__init__() - self.config = config - self.lastfm = None - self.last_start_time = None - - def on_start(self): - try: - self.lastfm = pylast.LastFMNetwork( - api_key=API_KEY, api_secret=API_SECRET, - username=self.config['scrobbler']['username'], - password_hash=pylast.md5(self.config['scrobbler']['password'])) - logger.info('Scrobbler connected to Last.fm') - except (pylast.NetworkError, pylast.MalformedResponseError, - pylast.WSError) as e: - logger.error('Error during Last.fm setup: %s', e) - self.stop() - - def track_playback_started(self, tl_track): - track = tl_track.track - artists = ', '.join([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) - try: - self.lastfm.update_now_playing( - artists, - (track.name or ''), - album=(track.album and track.album.name or ''), - duration=str(duration), - track_number=str(track.track_no), - mbid=(track.musicbrainz_id or '')) - except (pylast.ScrobblingError, pylast.NetworkError, - pylast.MalformedResponseError, pylast.WSError) as e: - logger.warning('Error submitting playing track to Last.fm: %s', e) - - def track_playback_ended(self, tl_track, time_position): - track = tl_track.track - artists = ', '.join([a.name for a in track.artists]) - duration = track.length and track.length // 1000 or 0 - time_position = time_position // 1000 - if duration < 30: - logger.debug('Track too short to scrobble. (30s)') - return - if time_position < duration // 2 and time_position < 240: - logger.debug( - 'Track not played long enough to scrobble. (50% or 240s)') - return - if self.last_start_time is None: - self.last_start_time = int(time.time()) - duration - logger.debug('Scrobbling track: %s - %s', artists, track.name) - try: - self.lastfm.scrobble( - artists, - (track.name or ''), - str(self.last_start_time), - album=(track.album and track.album.name or ''), - track_number=str(track.track_no), - duration=str(duration), - mbid=(track.musicbrainz_id or '')) - except (pylast.ScrobblingError, pylast.NetworkError, - pylast.MalformedResponseError, pylast.WSError) as e: - logger.warning('Error submitting played track to Last.fm: %s', e) diff --git a/mopidy/frontends/scrobbler/ext.conf b/mopidy/frontends/scrobbler/ext.conf deleted file mode 100644 index 4fded92f..00000000 --- a/mopidy/frontends/scrobbler/ext.conf +++ /dev/null @@ -1,4 +0,0 @@ -[scrobbler] -enabled = true -username = -password = diff --git a/setup.py b/setup.py index c5eea724..7cfb7409 100644 --- a/setup.py +++ b/setup.py @@ -29,7 +29,7 @@ setup( ], extras_require={ 'spotify': ['pyspotify >= 1.9, < 2'], - 'scrobbler': ['pylast >= 0.5.7'], + 'scrobbler': ['Mopidy-Scrobbler'], 'http': ['cherrypy >= 3.2.2', 'ws4py >= 0.2.3'], }, test_suite='nose.collector', @@ -45,7 +45,6 @@ setup( ], 'mopidy.ext': [ 'http = mopidy.frontends.http:Extension [http]', - 'scrobbler = mopidy.frontends.scrobbler:Extension [scrobbler]', 'local = mopidy.backends.local:Extension', 'mpd = mopidy.frontends.mpd:Extension', 'mpris = mopidy.frontends.mpris:Extension',