Skip to content

Commit

Permalink
Fix #42: Support LibraryProvider.get_images().
Browse files Browse the repository at this point in the history
  • Loading branch information
tkem committed Nov 21, 2015
1 parent 3c5d5e5 commit e8396a6
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 80 deletions.
96 changes: 44 additions & 52 deletions mopidy_internetarchive/library.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,18 @@

import collections
import logging
import operator

from mopidy import backend, models

import uritools

from . import Extension, translator

logger = logging.getLogger(__name__)


def _find(mapping, keys, default=None):
for key in keys:
if key in mapping:
return mapping[key]
return default


class InternetArchiveLibraryProvider(backend.LibraryProvider):

root_directory = models.Ref.directory(
uri=uritools.uricompose(Extension.ext_name),
name='Internet Archive'
uri=translator.uri(''), name='Internet Archive'
)

def __init__(self, config, backend):
Expand All @@ -39,21 +28,40 @@ def __init__(self, config, backend):
self.__lookup = {} # track cache for faster lookup

def browse(self, uri):
parts = uritools.urisplit(uri)
if parts.query:
return self.__browse_collection(parts.path, **parts.getquerydict())
elif parts.path:
return self.__browse_item(self.backend.client.getitem(parts.path))
identifier, filename, query = translator.parse_uri(uri)
if filename:
return []
elif query:
return self.__browse_collection(identifier, **query)
elif identifier:
return self.__browse_item(identifier)
else:
return self.__browse_root()

def get_images(self, uris):
client = self.backend.client
urimap = collections.defaultdict(list)
for uri in uris:
identifier, _, _ = translator.parse_uri(uri)
if identifier:
urimap[identifier].append(uri)
else:
logger.warn('No images for %s', uri)
results = {}
formats = self.__config['image_formats']
for identifier in urimap:
item = client.getitem(identifier)
images = translator.images(item, formats, client.geturl)
results.update(dict.fromkeys(urimap[identifier], images))
return results

def lookup(self, uri):
try:
return [self.__lookup[uri]]
except KeyError:
logger.debug("Lookup cache miss for %r", uri)
try:
_, _, identifier, _, filename = uritools.urisplit(uri)
identifier, filename, _ = translator.parse_uri(uri)
tracks = self.__tracks(self.backend.client.getitem(identifier))
self.__lookup = trackmap = {t.uri: t for t in tracks}
return [trackmap[uri]] if filename else tracks
Expand All @@ -62,8 +70,9 @@ def lookup(self, uri):
return []

def refresh(self, uri=None):
if self.backend.client.cache:
self.backend.client.cache.clear()
client = self.backend.client
if client.cache:
client.cache.clear()
self.__lookup.clear()

def search(self, query=None, uris=None, exact=False):
Expand All @@ -88,7 +97,7 @@ def search(self, query=None, uris=None, exact=False):
sort=self.__config['search_order']
)
return models.SearchResult(
uri=uritools.uricompose(Extension.ext_name, query=result.query),
uri=translator.uri(q=result.query),
albums=map(translator.album, result)
)

Expand All @@ -104,25 +113,16 @@ def __browse_collection(self, identifier, q=[], sort=['downloads desc']):
sort=sort
)))

def __browse_item(self, item):
metadata = item['metadata']
if metadata.get('mediatype') != 'collection':
def __browse_item(self, identifier):
item = self.backend.client.getitem(identifier)
if item['metadata']['mediatype'] != 'collection':
tracks = self.__tracks(item)
self.__lookup = {t.uri: t for t in tracks} # cache tracks
return [models.Ref.track(uri=t.uri, name=t.name) for t in tracks]
elif 'members' in item:
return list(map(translator.ref, item['members']))
else:
return self.__browse_views(metadata['identifier'])

def __browse_views(self, identifier, scheme=Extension.ext_name):
refs = []
for order, name in self.__config['browse_views'].items():
uri = uritools.uricompose(scheme, path=identifier, query={
'sort': order
})
refs.append(models.Ref.directory(name=name, uri=uri))
return refs
return self.__views(identifier)

def __browse_root(self):
# TODO: cache this
Expand All @@ -144,22 +144,14 @@ def __browse_root(self):
refs.append(translator.ref(obj))
return refs

def __tracks(self, item, namegetter=operator.itemgetter('name')):
client = self.backend.client
metadata = item['metadata']
files = collections.defaultdict(dict)
# HACK: not all files have "mtime", but reverse-sorting on
# filename tends to preserve "l(e)ast derived" versions,
# e.g. "filename.mp3" over "filename_vbr.mp3"
for file in sorted(item['files'], key=namegetter, reverse=True):
original = str(file.get('original', file['name']))
files[file['format']][original] = file
images = []
for file in _find(files, self.__config['image_formats'], {}).values():
images.append(client.geturl(metadata['identifier'], file['name']))
album = translator.album(metadata, images)
tracks = []
for file in _find(files, self.__config['audio_formats'], {}).values():
tracks.append(translator.track(metadata, file, album))
tracks.sort(key=lambda t: (t.track_no or 0, t.uri))
def __tracks(self, item, key=lambda t: (t.track_no or 0, t.uri)):
tracks = translator.tracks(item, self.__config['audio_formats'])
tracks.sort(key=key)
return tracks

def __views(self, identifier):
refs = []
for order, name in self.__config['browse_views'].items():
uri = translator.uri(identifier, sort=order)
refs.append(models.Ref.directory(name=name, uri=uri))
return refs
94 changes: 66 additions & 28 deletions mopidy_internetarchive/translator.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from __future__ import unicode_literals

import collections
import datetime
import logging
import re

from mopidy.models import Album, Artist, Ref, Track
from mopidy.models import Album, Artist, Image, Ref, Track

import uritools

Expand Down Expand Up @@ -102,20 +103,25 @@ def parse_creator(obj, default=None):
return [Artist(name=name) for name in obj]


def uri(identifier=None, filename=None, q=None):
if q:
return uritools.uricompose(Extension.ext_name, query={'q': q})
elif filename:
return '%s:%s#%s' % (Extension.ext_name, identifier, filename)
def parse_uri(uri):
parts = uritools.urisplit(uri)
return parts.path, parts.getfragment(), parts.getquerydict()


def uri(identifier='', filename=None, scheme=Extension.ext_name, **kwargs):
# filename may contain whitespace, e.g. PattiSmith1971
if filename:
return uritools.uricompose(scheme, path=identifier, fragment=filename)
elif kwargs:
return uritools.uricompose(scheme, path=identifier, query=kwargs)
else:
return '%s:%s' % (Extension.ext_name, identifier)
return '%s:%s' % (scheme, identifier)


def ref(obj, uri=uri):
identifier = obj['identifier']
mediatype = obj['mediatype']
name = obj.get('title', identifier)

if mediatype == 'search':
return Ref.directory(name=name, uri=uri(q=identifier))
elif mediatype != 'collection':
Expand All @@ -126,33 +132,65 @@ def ref(obj, uri=uri):
return Ref.directory(name=name, uri=uri(identifier))


def album(obj, images=[], uri=uri):
identifier = obj['identifier']
def artists(obj):
artist = obj.get('artist', obj.get('creator'))
if not artist:
return None
elif isinstance(artist, basestring):
return [Artist(name=artist)]
else:
return [Artist(name=name) for name in artist]


def album(obj, uri=uri):
identifier = obj['identifier']
return Album(
uri=uri(identifier),
name=obj.get('title', identifier),
artists=parse_creator(obj.get('creator')),
date=parse_date(obj.get('date')),
images=images
artists=artists(obj),
date=parse_date(obj.get('date'))
)


def track(obj, file, album, uri=uri):
identifier = obj['identifier']
filename = file['name']

return Track(
uri=uri(identifier, filename),
name=file.get('title', filename),
album=album,
artists=album.artists,
track_no=parse_track(file.get('track')),
date=parse_date(file.get('date'), album.date),
length=parse_length(file.get('length')),
bitrate=parse_bitrate(file.get('bitrate')),
last_modified=parse_mtime(file.get('mtime'))
)
def files(item, formats):
byname = {}
byformat = collections.defaultdict(list)
for obj in item['files']:
byname[obj['name']] = obj
byformat[obj['format']].append(obj)
try:
files = next(byformat[f] for f in formats if f in byformat)
except StopIteration:
return []
else:
return [dict(byname.get(f.get('original'), {}), **f) for f in files]


def images(item, formats, uri=uri):
identifier = item['metadata']['identifier']
images = []
for obj in files(item, formats):
images.append(Image(uri=uri(identifier, obj['name'])))
return images


def tracks(item, formats, uri=uri):
identifier = item['metadata']['identifier']
track = Track(album=album(item['metadata']))
tracks = []
for obj in files(item, formats):
name = obj['name']
tracks.append(track.replace(
uri=uri(identifier, name),
name=obj.get('title', name),
artists=artists(obj) or track.album.artists,
genre=obj.get('genre'),
track_no=parse_track(obj.get('track')),
length=parse_length(obj.get('length')),
bitrate=parse_bitrate(obj.get('bitrate')),
last_modified=parse_mtime(obj.get('mtime'))
))
return tracks


def query(query, uris=None, exact=False):
Expand Down

0 comments on commit e8396a6

Please sign in to comment.