Skip to content

Commit

Permalink
Merge pull request #16 from sumpfralle/mopidy_beets-browse_library
Browse files Browse the repository at this point in the history
enable "browsing" feature for Beets library
  • Loading branch information
sumpfralle committed May 21, 2016
2 parents 4b21125 + 2bc5a3e commit 7c60ebe
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 8 deletions.
28 changes: 25 additions & 3 deletions mopidy_beets/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,9 @@ def get_tracks(self):
return False

@cache(ctl=16)
def get_track(self, id, remote_url=False):
return self._convert_json_data(self._get('/item/%s' % id), remote_url)
def get_track(self, track_id, remote_url=False):
return self._convert_json_data(self._get('/item/%s' % track_id),
remote_url)

@cache()
def get_item_by(self, name):
Expand Down Expand Up @@ -102,6 +103,27 @@ def get_artists(self):
except KeyError:
return False

@cache()
def get_track_by_album_id(self, album_id):
tracks = self._get('/item/')["items"]
filtered_tracks = [track for track in tracks
if track["album_id"] == album_id]
return self._parse_query(filtered_tracks)

@cache()
def _get_album_by_attribute(self, attribute, value):
return [album for album in (self.get_album_by(value) or [])]

@cache()
def get_album_by_artist(self, artist):
return self._get_album_by_attribute("albumartist", artist)

@cache()
def get_album_artists(self):
albums = self._get('/album/')["albums"]
# remove duplicates
return list(set([album["albumartist"] for album in albums]))

@cache()
def get_album_by(self, name):
if isinstance(name, unicode):
Expand All @@ -122,9 +144,9 @@ def _get(self, url):

return req.json()
except Exception as e:
return False
logger.error('Request %s, failed with error %s' % (
url, e))
return False

def _parse_query(self, res):
if len(res) > 0:
Expand Down
56 changes: 51 additions & 5 deletions mopidy_beets/library.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from __future__ import unicode_literals

import logging
import urllib

from mopidy import backend
from mopidy import backend, models
from mopidy.models import SearchResult


Expand All @@ -11,6 +12,9 @@

class BeetsLibraryProvider(backend.LibraryProvider):

root_directory = models.Ref.directory(uri="beets:library",
name='Beets library')

def __init__(self, *args, **kwargs):
super(BeetsLibraryProvider, self).__init__(*args, **kwargs)
self.remote = self.backend.beets_api
Expand Down Expand Up @@ -41,6 +45,48 @@ def _find_exact(self, query=None, uris=None):
tracks = []
return SearchResult(uri='beets:tracks', tracks=tracks)

def browse(self, uri):
def quoting(text):
return urllib.quote(text.encode("utf-8"))

def unquoting(text):
return urllib.unquote(text.encode("ascii")).decode("utf-8")

def get_uri_path(*args):
return ":".join([self.root_directory.uri] + list(args))

# ignore the first two tokens
current_path = uri.split(":", 2)[-1]
if uri == self.root_directory.uri:
directories = {"albums-by-artist": "Albums by Artist"}
return [models.Ref.directory(uri=get_uri_path(uri_suffix),
name=label)
for uri_suffix, label in directories.items()]
elif current_path == "albums-by-artist":
# list all artists with albums
album_artists = self.remote.get_album_artists()
return [models.Ref.directory(uri=get_uri_path("albums-by-artist",
quoting(artist)),
name=artist)
for artist in album_artists]
elif current_path.startswith("albums-by-artist:"):
artist = unquoting(current_path.split(":", 1)[1])
albums_of_artist = self.remote.get_album_by_artist(artist)
albums_of_artist.sort(key=(lambda item: item["albumartist_sort"]))
return [models.Ref.directory(uri=get_uri_path("album",
str(album["id"])),
name=album["album"])
for album in albums_of_artist]
elif current_path.startswith("album:"):
album_id = int(current_path.split(":", 1)[1])
tracks_of_album = self.remote.get_track_by_album_id(album_id)
tracks_of_album.sort(key=(lambda item: item.track_no))
return [models.Ref.track(uri=track.uri, name=track.name)
for track in tracks_of_album]
else:
logger.error('Invalid browse URI: %s / %s', uri, current_path)
return []

def search(self, query=None, uris=None, exact=False):
logger.debug('Query "%s":' % query)
if not self.remote.has_connection:
Expand Down Expand Up @@ -78,15 +124,15 @@ def search(self, query=None, uris=None, exact=False):

def lookup(self, uri):
try:
id = uri.split(";")[1]
logger.debug('Beets track id for "%s": %s' % (id, uri))
return [self.remote.get_track(id, True)]
track_id = uri.split(";")[1]
logger.debug('Beets track id for "%s": %s' % (track_id, uri))
return [self.remote.get_track(track_id, True)]
except Exception as error:
logger.debug('Failed to lookup "%s": %s' % (uri, error))
return []

def _validate_query(self, query):
for (_, values) in query.iteritems():
for values in query.values():
if not values:
raise LookupError('Missing query')
for value in values:
Expand Down

0 comments on commit 7c60ebe

Please sign in to comment.