It will queue new tracks chosen among artists similar to your current queued
tracks, provided a title is found in your music library.
-Similar artists are fetched from last.fm or EchoNest webservice.
+Similar artists are fetched from last.fm webservice.
MPD_sima can queue track, top track or whole album for similar artists.
import sima.lib.cache
-SERVICE = ('EchoNest', 'LastFM')
-
def parse_cache_control(headers):
"""
Parse the cache control headers returning a dictionary with values
homedir = environ.get('HOME')
xdh = environ.get('XDG_DATA_HOME', pjoin(homedir, '.local/share/'))
-default_cache = pjoin(xdh, 'mpd_sima/http/', SERVICE[1])
+default_cache = pjoin(xdh, 'mpd_sima/http/LastFM')
cache = environ.get('SIMA_VARDIR', default_cache)
if len(sys.argv) > 1:
.\" Title: mpd-sima
.\" Author: Jack Kaliko <kaliko@azylum.org>
.\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\" Date: 11/18/2017
+.\" Date: 11/21/2017
.\" Manual: mpd-sima 0.14.5 User Manual
.\" Source: mpd-sima
.\" Language: English
.\"
-.TH "MPD\-SIMA" "1" "11/18/2017" "mpd-sima" "mpd-sima 0.14.5 User Manual"
+.TH "MPD\-SIMA" "1" "11/21/2017" "mpd-sima" "mpd-sima 0.14.5 User Manual"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
.\" Title: mpd_sima.cfg
.\" Author: Jack Kaliko <kaliko@azylum.org>
.\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\" Date: 11/18/2017
+.\" Date: 11/21/2017
.\" Manual: mpd-sima 0.14.5 User Manual
.\" Source: mpd-sima
.\" Language: English
.\"
-.TH "MPD_SIMA\&.CFG" "5" "11/18/2017" "mpd-sima" "mpd-sima 0.14.5 User Manual"
+.TH "MPD_SIMA\&.CFG" "5" "11/21/2017" "mpd-sima" "mpd-sima 0.14.5 User Manual"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
.sp
You can add here as many plugins you want, currently shipping
\fBLastfm\fR
-and
-\fBEchoNest\fR
only\&.
.br
.RS 4
Plugin priority
.RE
-.SS "EchoNest section"
-.PP
-EchoNest plugin\*(Aqs configuration\&.
-.PP
-Options for EchoNest are exactly the same as LastFm (same default as well), except for cache plugin which is always needed to limit number of requests to the service\&.
-.PP
-\fB[echonest]\fR
-.RS 4
-.RE
-.PP
-\fBqueue_mode=\fR\fItrack\fR
-.RS 4
-.RE
-.PP
-\fBmax_art=\fR\fI10\fR
-.RS 4
-.RE
-.PP
-\fBdepth=\fR\fI1\fR
-.RS 4
-.RE
-.PP
-\fBsingle_album=\fR\fIfalse\fR
-.RS 4
-.RE
-.PP
-\fBtrack_to_add=\fR\fI1\fR
-.RS 4
-.RE
-.PP
-\fBalbum_to_add=\fR\fI1\fR
-.RS 4
-.RE
-.PP
-\fBpriority=\fR\fI100\fR
-.RS 4
-.RE
.SH "QUEUE MODES"
.PP
mpd\-sima offers different queue modes\&. All of them pick up tracks from artists similar to the last track in the queue\&.
<option>Random</option> will queue a track at random if other plugins did not return any tracks.<sbr />
</para>
<para>You can add here as many plugins you want,
- currently shipping <option>Lastfm</option> and
- <option>EchoNest</option> only.<sbr />
+ currently shipping <option>Lastfm</option> only.<sbr />
The priority may be used to order them.
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect2>
- <refsect2 id="echonest">
- <title>EchoNest section</title>
- <para>EchoNest plugin's configuration.</para>
- <para>Options for EchoNest are exactly the same as LastFm (same
- default as well), except for cache plugin which is always
- needed to limit number of requests to the service.</para>
- <variablelist>
- <varlistentry> <!-- echonest -->
- <term><option>[echonest]</option></term>
- </varlistentry>
- <varlistentry> <!-- echonest.queue_mode -->
- <term><option>queue_mode=</option><replaceable>track</replaceable></term>
- </varlistentry>
- <varlistentry> <!-- echonest.max_art -->
- <term><option>max_art=</option><replaceable>10</replaceable></term>
- </varlistentry>
- <varlistentry> <!-- echonest.depth -->
- <term><option>depth=</option><replaceable>1</replaceable></term>
- </varlistentry>
- <varlistentry> <!-- echonest.single_album -->
- <term><option>single_album=</option><replaceable>false</replaceable></term>
- </varlistentry>
- <varlistentry> <!-- echonest.track_to_add -->
- <term><option>track_to_add=</option><replaceable>1</replaceable></term>
- </varlistentry>
- <varlistentry> <!-- echonest.album_to_add -->
- <term><option>album_to_add=</option><replaceable>1</replaceable></term>
- </varlistentry>
- <varlistentry> <!-- echonest.priority -->
- <term><option>priority=</option><replaceable>100</replaceable></term>
- </varlistentry>
- </variablelist>
- </refsect2>
</refsect1>
<refsect1 id="queue_mode">
<title>QUEUE MODES</title>
.\" Title: simadb_cli
.\" Author: Jack Kaliko <kaliko@azylum.org>
.\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\" Date: 11/18/2017
+.\" Date: 11/21/2017
.\" Manual: mpd-sima 0.14.5 User Manual
.\" Source: mpd-sima
.\" Language: English
.\"
-.TH "SIMADB_CLI" "1" "11/18/2017" "mpd-sima" "mpd-sima 0.14.5 User Manual"
+.TH "SIMADB_CLI" "1" "11/21/2017" "mpd-sima" "mpd-sima 0.14.5 User Manual"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
MPD_sima v0.15.0
+ * Remove EchoNest support (Closes #10)
* Enhanced queuing behavior in random mode (Closes #16)
-- kaliko jack <kaliko@azylum.org> UNRELEASED
#
# Possible values for internal
# Lastfm : Queue using last.fm web service for suggestions.
-# EchoNest : Queue using EchoNest web service for suggestions.
# Random : Queue a track at random (different flavour, cf. documentation)
# Crop : Remove old tracks from queue
#
#track_to_add = 1
-# EchoNest or Lastfm
-#[echonest]
[lastfm]
## QUEUE_MODE
# type: string
# * When set to "true", sima will use a persistent cache for its http client.
# The cache is written along with the dbfile in:
# $XDG_CONFIG_HOME/mpd_sima/http/<web_service>
-# Toggling http cache is only available for last.fm. EchoNest have rate limits,
-# we must then pay attention to bandwidth and use of caching is required.
+# Until #7 is closed LastFM is broken; cf. https://gitlab.com/kaliko/sima/issues/7
# * If set to "false", caching is still done but in memory.
# default: True
cache = True
'host':'ws.audioscrobbler.com',
'version': '2.0',}
-ECH = {'apikey': 'WlRKQkhTS0JHWFVDUEZZRFA',
- 'host': 'developer.echonest.com',
- 'version': 'v4',}
-
WAIT_BETWEEN_REQUESTS = timedelta(days=0, seconds=2)
SOCKET_TIMEOUT = 6
MPD_sima is a non interactive MPD autoqueue client running in the background.
It will queue new tracks chosen among artists similar to your current queued
tracks, provided a title is found in your music library.
-Similar artists are fetched from last.fm or EchoNest webservice.
+Similar artists are fetched from last.fm webservice.
MPD_sima can queue track, top track or whole album for similar artists.
"""
-__version__ = '0.14.5'
+__version__ = '0.15.0'
__author__ = 'kaliko jack'
__email__ = 'kaliko@azylum.org'
__url__ = 'git://git.kaliko.me/sima.git'
resp = self.controller.update_cached_response(prepreq, resp)
elif resp.status_code != 200:
raise WSHTTPError('{0.status_code}: {0.reason}'.format(resp))
- ratelimit = resp.headers.get('x-ratelimit-remaining', None)
- if ratelimit and self.stats:
- minrl = min(int(ratelimit), self.stats.get('minrl'))
- self.stats.update(minrl=minrl)
self.controller.cache_response(resp.request, resp)
return resp
+++ /dev/null
-# -*- coding: utf-8 -*-
-
-# Copyright (c) 2014 Jack Kaliko <kaliko@azylum.org>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-#
-#
-
-"""
-Consume EchoNest web service
-"""
-
-__version__ = '0.0.5'
-__author__ = 'Jack Kaliko'
-
-
-
-from sima import ECH
-from sima.lib.meta import Artist
-from sima.lib.track import Track
-from sima.lib.http import HttpClient
-from sima.utils.utils import WSError, WSNotFound
-from sima.utils.utils import getws
-if len(ECH.get('apikey')) == 23: # simple hack allowing imp.reload
- getws(ECH)
-
-
-def get_mbid(obj, foreign='foreign_ids'):
- if foreign in obj:
- for frgnid in obj.get(foreign):
- if frgnid.get('catalog') == 'musicbrainz':
- return frgnid.get('foreign_id').split(':')[2]
- return None
-
-
-class SimaEch:
- """EchoNest http client
- """
- root_url = 'http://{host}/api/{version}'.format(**ECH)
- name = 'EchoNest'
- cache = False
- """HTTP cache to use, in memory or persitent.
-
- :param BaseCache cache: Set a cache, defaults to `False`.
- """
- stats = {'etag':0,
- 'ccontrol':0,
- 'minrl':120,
- 'total':0}
-
- def __init__(self):
- self.http = HttpClient(cache=self.cache, stats=self.stats)
-
- def _controls_answer(self, ans):
- """Controls answer.
- """
- status = ans.get('response').get('status')
- code = status.get('code')
- if code is 0:
- return True
- if code is 5:
- raise WSNotFound('Artist not found')
- raise WSError(status.get('message'))
-
- def _forge_payload(self, artist, top=False):
- """Build payload
- """
- payload = {'api_key': ECH.get('apikey')}
- if not isinstance(artist, Artist):
- raise TypeError('"{0!r}" not an Artist object'.format(artist))
- if artist.mbid:
- payload.update(id='musicbrainz:artist:{0}'.format(artist.mbid))
- else:
- payload.update(name=artist.name)
- payload.update(bucket='id:musicbrainz')
- payload.update(results=100)
- if top:
- if artist.mbid:
- aid = payload.pop('id')
- payload.update(artist_id=aid)
- else:
- name = payload.pop('name')
- payload.update(artist=name)
- payload.update(results=100)
- payload.update(sort='song_hotttnesss-desc')
- # > hashing the URL into a cache key
- # return a sorted list of 2-tuple to have consistent cache
- return sorted(payload.items(), key=lambda param: param[0])
-
- def get_similar(self, artist):
- """Fetch similar artists
-
- :param sima.lib.meta.Artist artist: `Artist` to fetch similar artists from
- :returns: generator of :class:`sima.lib.meta.Artist`
- """
- payload = self._forge_payload(artist)
- # Construct URL
- ressource = '{0}/artist/similar'.format(SimaEch.root_url)
- ans = self.http(ressource, payload)
- self._controls_answer(ans.json()) # pylint: disable=no-member
- for art in ans.json().get('response').get('artists'): # pylint: disable=no-member
- mbid = get_mbid(art)
- yield Artist(mbid=mbid, name=art.get('name'))
-
- def get_toptrack(self, artist):
- """Fetch artist top tracks
-
- :param sima.lib.meta.Artist artist: `Artist` to fetch top tracks from
- :returns: generator of :class:`sima.lib.track.Track`
- """
- payload = self._forge_payload(artist, top=True)
- # Construct URL
- ressource = '{0}/song/search'.format(SimaEch.root_url)
- ans = self.http(ressource, payload)
- self._controls_answer(ans.json()) # pylint: disable=no-member
- titles = list()
- art = {'artist': artist.name,
- 'musicbrainz_artistid': artist.mbid,}
- for song in ans.json().get('response').get('songs'): # pylint: disable=no-member
- title = song.get('title')
- if not art.get('musicbrainz_artistid'):
- art['musicbrainz_artistid'] = get_mbid(song, 'artist_foreign_ids')
- if title not in titles:
- titles.append(title)
- yield Track(title=title, **art)
-
-
-# VIM MODLINE
-# vim: ai ts=4 sw=4 sts=4 expandtab
+++ /dev/null
-# -*- coding: utf-8 -*-
-# Copyright (c) 2013, 2014 Jack Kaliko <kaliko@azylum.org>
-#
-# This file is part of sima
-#
-# sima is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# sima is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with sima. If not, see <http://www.gnu.org/licenses/>.
-#
-#
-"""
-Fetching similar artists from echonest web services
-"""
-
-# standard library import
-from os.path import join
-
-# third parties components
-
-# local import
-from ...lib.simaecho import SimaEch
-from ...lib.webserv import WebService
-from ...lib.cache import FileCache
-
-
-class EchoNest(WebService):
- """EchoNest similar artists http API
- """
-
- def __init__(self, daemon):
- WebService.__init__(self, daemon)
- # Set persitent cache
- vardir = daemon.config['sima']['var_dir']
- SimaEch.cache = FileCache(join(vardir, 'http', 'EchoNest'))
- self.ws = SimaEch()
-
-# VIM MODLINE
-# vim: ai ts=4 sw=4 sts=4 expandtab
'consume': 10,
'priority': 0,
},
- 'echonest': {
- 'queue_mode': "track", #TODO control values
- 'max_art': 15,
- 'single_album': "false",
- 'track_to_add': 1,
- 'album_to_add': 1,
- 'depth': 1,
- 'priority': 100,
- },
'lastfm': {
'queue_mode': "track", #TODO control values
'max_art': 10,