Merge branch 'master' of https://www.github.com/Prior99/mopidy-subidy into urlencode_uris
This commit is contained in:
commit
9e8116713c
4 changed files with 151 additions and 21 deletions
|
@ -31,7 +31,6 @@ The following things are **not** supported:
|
||||||
* Creating, editing and deleting playlists
|
* Creating, editing and deleting playlists
|
||||||
* Subsonics smart playlists
|
* Subsonics smart playlists
|
||||||
* Searching for a combination of filters (artist and album, artist and track, etc.)
|
* Searching for a combination of filters (artist and album, artist and track, etc.)
|
||||||
* Browsing more than 2 levels deep in the Subsonic directory tree
|
|
||||||
|
|
||||||
## Contributors
|
## Contributors
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,35 @@ import logging
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class SubidyLibraryProvider(backend.LibraryProvider):
|
class SubidyLibraryProvider(backend.LibraryProvider):
|
||||||
root_directory = Ref.directory(uri=uri.ROOT_URI, name='Subsonic')
|
def __create_vdirs():
|
||||||
|
vdir_templates = [
|
||||||
|
dict(id="root", name="Subsonic"),
|
||||||
|
dict(id="artists", name="Artists"),
|
||||||
|
dict(id="albums", name="Albums"),
|
||||||
|
dict(id="rootdirs", name="Directories"),
|
||||||
|
]
|
||||||
|
# Create a dict with the keys being the `id`s in `vdir_templates`
|
||||||
|
# and the values being objects containing the vdir `id`,
|
||||||
|
# the human readable name as `name`, and the URI as `uri`.
|
||||||
|
vdirs = {}
|
||||||
|
for template in vdir_templates:
|
||||||
|
vdir = template.copy()
|
||||||
|
vdir.update(uri=uri.get_vdir_uri(vdir["id"]))
|
||||||
|
vdirs[template['id']] = vdir
|
||||||
|
return vdirs
|
||||||
|
|
||||||
|
_vdirs = __create_vdirs()
|
||||||
|
|
||||||
|
def __raw_vdir_to_ref(vdir):
|
||||||
|
if vdir is None:
|
||||||
|
return None
|
||||||
|
return Ref.directory(
|
||||||
|
name=vdir['name'],
|
||||||
|
uri=vdir['uri'])
|
||||||
|
|
||||||
|
root_directory = __raw_vdir_to_ref(_vdirs['root'])
|
||||||
|
|
||||||
|
_raw_vdir_to_ref = staticmethod(__raw_vdir_to_ref)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(SubidyLibraryProvider, self).__init__(*args, **kwargs)
|
super(SubidyLibraryProvider, self).__init__(*args, **kwargs)
|
||||||
|
@ -15,12 +43,18 @@ class SubidyLibraryProvider(backend.LibraryProvider):
|
||||||
def browse_songs(self, album_id):
|
def browse_songs(self, album_id):
|
||||||
return self.subsonic_api.get_songs_as_refs(album_id)
|
return self.subsonic_api.get_songs_as_refs(album_id)
|
||||||
|
|
||||||
def browse_albums(self, artist_id):
|
def browse_albums(self, artist_id=None):
|
||||||
return self.subsonic_api.get_albums_as_refs(artist_id)
|
return self.subsonic_api.get_albums_as_refs(artist_id)
|
||||||
|
|
||||||
def browse_artists(self):
|
def browse_artists(self):
|
||||||
return self.subsonic_api.get_artists_as_refs()
|
return self.subsonic_api.get_artists_as_refs()
|
||||||
|
|
||||||
|
def browse_rootdirs(self):
|
||||||
|
return self.subsonic_api.get_rootdirs_as_refs()
|
||||||
|
|
||||||
|
def browse_diritems(self, directory_id):
|
||||||
|
return self.subsonic_api.get_diritems_as_refs(directory_id)
|
||||||
|
|
||||||
def lookup_song(self, song_id):
|
def lookup_song(self, song_id):
|
||||||
return self.subsonic_api.get_song_by_id(song_id)
|
return self.subsonic_api.get_song_by_id(song_id)
|
||||||
|
|
||||||
|
@ -31,13 +65,27 @@ class SubidyLibraryProvider(backend.LibraryProvider):
|
||||||
return self.subsonic_api.get_artist_by_id(artist_id)
|
return self.subsonic_api.get_artist_by_id(artist_id)
|
||||||
|
|
||||||
def browse(self, browse_uri):
|
def browse(self, browse_uri):
|
||||||
type = uri.get_type(browse_uri)
|
if browse_uri == uri.get_vdir_uri('root'):
|
||||||
if browse_uri == uri.ROOT_URI:
|
root_vdir_names = ["rootdirs", "artists", "albums"]
|
||||||
|
root_vdirs = [self._vdirs[vdir_name] for vdir_name in root_vdir_names]
|
||||||
|
sorted_root_vdirs = sorted(root_vdirs, key=lambda vdir: vdir["name"])
|
||||||
|
return [self._raw_vdir_to_ref(vdir) for vdir in sorted_root_vdirs]
|
||||||
|
elif browse_uri == uri.get_vdir_uri("rootdirs"):
|
||||||
|
return self.browse_rootdirs()
|
||||||
|
elif browse_uri == uri.get_vdir_uri("artists"):
|
||||||
return self.browse_artists()
|
return self.browse_artists()
|
||||||
if type == uri.ARTIST:
|
elif browse_uri == uri.get_vdir_uri("albums"):
|
||||||
|
return self.browse_albums()
|
||||||
|
else:
|
||||||
|
uri_type = uri.get_type(browse_uri)
|
||||||
|
if uri_type == uri.DIRECTORY:
|
||||||
|
return self.browse_diritems(uri.get_directory_id(browse_uri))
|
||||||
|
elif uri_type == uri.ARTIST:
|
||||||
return self.browse_albums(uri.get_artist_id(browse_uri))
|
return self.browse_albums(uri.get_artist_id(browse_uri))
|
||||||
if type == uri.ALBUM:
|
elif uri_type == uri.ALBUM:
|
||||||
return self.browse_songs(uri.get_album_id(browse_uri))
|
return self.browse_songs(uri.get_album_id(browse_uri))
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
|
||||||
def lookup_one(self, lookup_uri):
|
def lookup_one(self, lookup_uri):
|
||||||
type = uri.get_type(lookup_uri)
|
type = uri.get_type(lookup_uri)
|
||||||
|
|
|
@ -14,6 +14,7 @@ UNKNOWN_SONG = u'Unknown Song'
|
||||||
UNKNOWN_ALBUM = u'Unknown Album'
|
UNKNOWN_ALBUM = u'Unknown Album'
|
||||||
UNKNOWN_ARTIST = u'Unknown Artist'
|
UNKNOWN_ARTIST = u'Unknown Artist'
|
||||||
MAX_SEARCH_RESULTS = 100
|
MAX_SEARCH_RESULTS = 100
|
||||||
|
MAX_LIST_RESULTS = 500
|
||||||
|
|
||||||
ref_sort_key = lambda ref: ref.name
|
ref_sort_key = lambda ref: ref.name
|
||||||
|
|
||||||
|
@ -32,7 +33,7 @@ def diritem_sort_key(item):
|
||||||
if isdir:
|
if isdir:
|
||||||
key = string_nums_nocase_sort_key(item['title'])
|
key = string_nums_nocase_sort_key(item['title'])
|
||||||
else:
|
else:
|
||||||
key = int(item['track'])
|
key = int(item.get('track', 1))
|
||||||
return (isdir, key)
|
return (isdir, key)
|
||||||
|
|
||||||
class SubsonicApi():
|
class SubsonicApi():
|
||||||
|
@ -100,12 +101,27 @@ class SubsonicApi():
|
||||||
albums=[self.raw_album_to_album(album) for album in result.get('album') or []],
|
albums=[self.raw_album_to_album(album) for album in result.get('album') or []],
|
||||||
tracks=[self.raw_song_to_track(song) for song in result.get('song') or []])
|
tracks=[self.raw_song_to_track(song) for song in result.get('song') or []])
|
||||||
|
|
||||||
|
|
||||||
def get_raw_artists(self):
|
def get_raw_artists(self):
|
||||||
|
try:
|
||||||
|
response = self.connection.getArtists()
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning('Connecting to subsonic failed when loading list of artists.')
|
||||||
|
return []
|
||||||
|
if response.get('status') != RESPONSE_OK:
|
||||||
|
logger.warning('Got non-okay status code from subsonic: %s' % response.get('status'))
|
||||||
|
return []
|
||||||
|
letters = response.get('artists').get('index')
|
||||||
|
if letters is not None:
|
||||||
|
artists = [artist for letter in letters for artist in letter.get('artist') or []]
|
||||||
|
return artists
|
||||||
|
logger.warning('Subsonic does not seem to have any artists in it\'s library.')
|
||||||
|
return []
|
||||||
|
|
||||||
|
def get_raw_rootdirs(self):
|
||||||
try:
|
try:
|
||||||
response = self.connection.getIndexes()
|
response = self.connection.getIndexes()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning('Connecting to subsonic failed when loading list of artists.')
|
logger.warning('Connecting to subsonic failed when loading list of rootdirs.')
|
||||||
return []
|
return []
|
||||||
if response.get('status') != RESPONSE_OK:
|
if response.get('status') != RESPONSE_OK:
|
||||||
logger.warning('Got non-okay status code from subsonic: %s' % response.get('status'))
|
logger.warning('Got non-okay status code from subsonic: %s' % response.get('status'))
|
||||||
|
@ -114,7 +130,7 @@ class SubsonicApi():
|
||||||
if letters is not None:
|
if letters is not None:
|
||||||
artists = [artist for letter in letters for artist in letter.get('artist') or []]
|
artists = [artist for letter in letters for artist in letter.get('artist') or []]
|
||||||
return artists
|
return artists
|
||||||
logger.warning('Subsonic does not seem to have any artists in it\'s library.')
|
logger.warning('Subsonic does not seem to have any rootdirs in its library.')
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def get_song_by_id(self, song_id):
|
def get_song_by_id(self, song_id):
|
||||||
|
@ -192,13 +208,50 @@ class SubsonicApi():
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_raw_albums(self, artist_id):
|
def get_raw_albums(self, artist_id):
|
||||||
return self.get_raw_dir(artist_id)
|
try:
|
||||||
|
response = self.connection.getArtist(artist_id)
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning('Connecting to subsonic failed when loading list of albums.')
|
||||||
|
return []
|
||||||
|
if response.get('status') != RESPONSE_OK:
|
||||||
|
logger.warning('Got non-okay status code from subsonic: %s' % response.get('status'))
|
||||||
|
return []
|
||||||
|
albums = response.get('artist').get('album')
|
||||||
|
if albums is not None:
|
||||||
|
return sorted(albums, key=lambda album: string_nums_nocase_sort_key(album['name']))
|
||||||
|
return []
|
||||||
|
|
||||||
def get_raw_songs(self, album_id):
|
def get_raw_songs(self, album_id):
|
||||||
return self.get_raw_dir(album_id)
|
try:
|
||||||
|
response = self.connection.getAlbum(album_id)
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning('Connecting to subsonic failed when loading list of songs in album.')
|
||||||
|
return []
|
||||||
|
if response.get('status') != RESPONSE_OK:
|
||||||
|
logger.warning('Got non-okay status code from subsonic: %s' % response.get('status'))
|
||||||
|
return []
|
||||||
|
songs = response.get('album').get('song')
|
||||||
|
if songs is not None:
|
||||||
|
return songs
|
||||||
|
return []
|
||||||
|
|
||||||
def get_albums_as_refs(self, artist_id):
|
def get_raw_album_list(self, ltype, size=MAX_LIST_RESULTS):
|
||||||
return [self.raw_album_to_ref(album) for album in self.get_raw_albums(artist_id)]
|
try:
|
||||||
|
response = self.connection.getAlbumList2(ltype=ltype, size=size)
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning('Connecting to subsonic failed when loading album list.')
|
||||||
|
return []
|
||||||
|
if response.get('status') != RESPONSE_OK:
|
||||||
|
logger.warning('Got non-okay status code from subsonic: %s' % response.get('status'))
|
||||||
|
return []
|
||||||
|
albums = response.get('albumList2').get('album')
|
||||||
|
if albums is not None:
|
||||||
|
return albums
|
||||||
|
return []
|
||||||
|
|
||||||
|
def get_albums_as_refs(self, artist_id=None):
|
||||||
|
albums = (self.get_raw_album_list('alphabeticalByName') if artist_id is None else self.get_raw_albums(artist_id))
|
||||||
|
return [self.raw_album_to_ref(album) for album in albums]
|
||||||
|
|
||||||
def get_albums_as_albums(self, artist_id):
|
def get_albums_as_albums(self, artist_id):
|
||||||
return [self.raw_album_to_album(album) for album in self.get_raw_albums(artist_id)]
|
return [self.raw_album_to_album(album) for album in self.get_raw_albums(artist_id)]
|
||||||
|
@ -212,6 +265,12 @@ class SubsonicApi():
|
||||||
def get_artists_as_refs(self):
|
def get_artists_as_refs(self):
|
||||||
return [self.raw_artist_to_ref(artist) for artist in self.get_raw_artists()]
|
return [self.raw_artist_to_ref(artist) for artist in self.get_raw_artists()]
|
||||||
|
|
||||||
|
def get_rootdirs_as_refs(self):
|
||||||
|
return [self.raw_directory_to_ref(rootdir) for rootdir in self.get_raw_rootdirs()]
|
||||||
|
|
||||||
|
def get_diritems_as_refs(self, directory_id):
|
||||||
|
return [(self.raw_directory_to_ref(diritem) if diritem.get('isDir') else self.raw_song_to_ref(diritem)) for diritem in self.get_raw_dir(directory_id)]
|
||||||
|
|
||||||
def get_artists_as_artists(self):
|
def get_artists_as_artists(self):
|
||||||
return [self.raw_artist_to_artist(artist) for artist in self.get_raw_artists()]
|
return [self.raw_artist_to_artist(artist) for artist in self.get_raw_artists()]
|
||||||
|
|
||||||
|
@ -273,6 +332,13 @@ class SubsonicApi():
|
||||||
name=album.get('artist'),
|
name=album.get('artist'),
|
||||||
uri=uri.get_artist_uri(album.get('artistId')))])
|
uri=uri.get_artist_uri(album.get('artistId')))])
|
||||||
|
|
||||||
|
def raw_directory_to_ref(self, directory):
|
||||||
|
if directory is None:
|
||||||
|
return None
|
||||||
|
return Ref.directory(
|
||||||
|
name=directory.get('title') or directory.get('name'),
|
||||||
|
uri=uri.get_directory_uri(directory.get('id')))
|
||||||
|
|
||||||
def raw_artist_to_ref(self, artist):
|
def raw_artist_to_ref(self, artist):
|
||||||
if artist is None:
|
if artist is None:
|
||||||
return None
|
return None
|
||||||
|
|
|
@ -4,12 +4,11 @@ SONG = 'song'
|
||||||
ARTIST = 'artist'
|
ARTIST = 'artist'
|
||||||
PLAYLIST = 'playlist'
|
PLAYLIST = 'playlist'
|
||||||
ALBUM = 'album'
|
ALBUM = 'album'
|
||||||
|
DIRECTORY = 'directory'
|
||||||
|
VDIR = 'vdir'
|
||||||
PREFIX = 'subidy'
|
PREFIX = 'subidy'
|
||||||
ROOT = 'root'
|
|
||||||
SEARCH = 'search'
|
SEARCH = 'search'
|
||||||
|
|
||||||
ROOT_URI = '%s:%s' % (PREFIX, ROOT)
|
|
||||||
|
|
||||||
regex = re.compile(r'(\w+?):(\w+?)(?::|$)(.+?)?$')
|
regex = re.compile(r'(\w+?):(\w+?)(?::|$)(.+?)?$')
|
||||||
|
|
||||||
def is_type_result_valid(result):
|
def is_type_result_valid(result):
|
||||||
|
@ -45,6 +44,18 @@ def get_album_id(uri):
|
||||||
return None
|
return None
|
||||||
return result.group(3)
|
return result.group(3)
|
||||||
|
|
||||||
|
def get_directory_id(uri):
|
||||||
|
result = regex.match(uri)
|
||||||
|
if not is_id_result_valid(result, DIRECTORY):
|
||||||
|
return None
|
||||||
|
return result.group(3)
|
||||||
|
|
||||||
|
def get_vdir_id(uri):
|
||||||
|
result = regex.match(uri)
|
||||||
|
if not is_id_result_valid(result, VDIR):
|
||||||
|
return None
|
||||||
|
return result.group(3)
|
||||||
|
|
||||||
def get_type(uri):
|
def get_type(uri):
|
||||||
result = regex.match(uri)
|
result = regex.match(uri)
|
||||||
if not is_type_result_valid(result):
|
if not is_type_result_valid(result):
|
||||||
|
@ -63,6 +74,12 @@ def get_album_uri(id):
|
||||||
def get_song_uri(id):
|
def get_song_uri(id):
|
||||||
return get_type_uri(SONG, id)
|
return get_type_uri(SONG, id)
|
||||||
|
|
||||||
|
def get_directory_uri(id):
|
||||||
|
return get_type_uri(DIRECTORY, id)
|
||||||
|
|
||||||
|
def get_vdir_uri(id):
|
||||||
|
return get_type_uri(VDIR, id)
|
||||||
|
|
||||||
def get_playlist_uri(id):
|
def get_playlist_uri(id):
|
||||||
return get_type_uri(PLAYLIST, id)
|
return get_type_uri(PLAYLIST, id)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue