scrobbler: Move to external extension
This commit is contained in:
parent
fd6bb4ba43
commit
6400a45a0e
@ -50,4 +50,3 @@ Frontend implementations
|
||||
* :mod:`mopidy.frontends.http`
|
||||
* :mod:`mopidy.frontends.mpd`
|
||||
* :mod:`mopidy.frontends.mpris`
|
||||
* :mod:`mopidy.frontends.scrobbler`
|
||||
|
||||
@ -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 <https://github.com/mopidy/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
|
||||
|
||||
@ -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 <https://pypi.python.org/pypi/Mopidy-Scrobbler>`_
|
||||
GitHub:
|
||||
`mopidy/mopidy-scrobbler <https://github.com/mopidy/mopidy-scrobbler>`_
|
||||
Issues:
|
||||
https://github.com/mopidy/mopidy-scrobbler/issues
|
||||
|
||||
|
||||
Mopidy-SomaFM
|
||||
-------------
|
||||
|
||||
|
||||
@ -1,55 +0,0 @@
|
||||
.. _ext-scrobbler:
|
||||
|
||||
****************
|
||||
Mopidy-Scrobbler
|
||||
****************
|
||||
|
||||
This extension scrobbles the music you play to your `Last.fm
|
||||
<http://www.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
|
||||
@ -25,9 +25,9 @@ Glossary
|
||||
frontend
|
||||
A part of Mopidy *using* the :term:`core` API. Existing frontends
|
||||
include the :ref:`MPD server <ext-mpd>`, the :ref:`MPRIS/D-Bus
|
||||
integration <ext-mpris>`, the :ref:`Last.fm scrobbler <ext-scrobbler>`,
|
||||
and the :ref:`HTTP server <ext-http>` with JavaScript API. See
|
||||
:ref:`frontend-api` for details.
|
||||
integration <ext-mpris>`, the Last.fm scrobbler, and the :ref:`HTTP
|
||||
server <ext-http>` with JavaScript API. See :ref:`frontend-api` for
|
||||
details.
|
||||
|
||||
mixer
|
||||
A GStreamer element that controls audio volume.
|
||||
|
||||
@ -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]
|
||||
@ -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)
|
||||
@ -1,4 +0,0 @@
|
||||
[scrobbler]
|
||||
enabled = true
|
||||
username =
|
||||
password =
|
||||
3
setup.py
3
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',
|
||||
|
||||
Loading…
Reference in New Issue
Block a user