179 lines
7.1 KiB
Python
179 lines
7.1 KiB
Python
from urlparse import urlparse
|
|
import libsonic
|
|
import logging
|
|
import itertools
|
|
from mopidy.models import Track, Album, Artist, Playlist, Ref
|
|
from mopidy_subidy import uri
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
RESPONSE_OK = u'ok'
|
|
UNKNOWN_SONG = u'Unknown Song'
|
|
UNKNOWN_ALBUM = u'Unknown Album'
|
|
UNKNOWN_ARTIST = u'Unknown Artist'
|
|
MAX_SEARCH_RESULTS = 100
|
|
|
|
ref_sort_key = lambda ref: ref.name
|
|
|
|
class SubsonicApi():
|
|
def __init__(self, url, username, password):
|
|
parsed = urlparse(url)
|
|
self.port = parsed.port if parsed.port else \
|
|
443 if parsed.scheme == 'https' else 80
|
|
base_url = parsed.scheme + '://' + parsed.hostname
|
|
self.connection = libsonic.Connection(
|
|
base_url,
|
|
username,
|
|
password,
|
|
self.port,
|
|
parsed.path + '/rest')
|
|
self.url = url + '/rest'
|
|
self.username = username
|
|
self.password = password
|
|
logger.info('Connecting to subsonic server on url %s as user %s' % (url, username))
|
|
try:
|
|
self.connection.ping()
|
|
except Exception as e:
|
|
logger.error('Unabled to reach subsonic server: %s' % e)
|
|
exit()
|
|
|
|
def get_song_stream_uri(self, song_id):
|
|
template = '%s/stream.view?id=%s&u=%s&p=%s&c=mopidy&v=1.14'
|
|
return template % (self.url, song_id, self.username, self.password)
|
|
|
|
def find_artists_by_name(self, artist_name):
|
|
response = self.connection.search3(artist_name, MAX_SEARCH_RESULTS, 0, 0, 0, 0, 0)
|
|
if response.get('status') != RESPONSE_OK:
|
|
return None
|
|
artists = response.get('searchResult3').get('artist')
|
|
if artists is not None:
|
|
return [self.raw_artist_to_artist(artist) for artist in artists]
|
|
return None
|
|
|
|
def find_tracks_by_name(self, track_name):
|
|
response = self.connection.search3(track_name, 0, 0, 0, 0, MAX_SEARCH_RESULTS, 0)
|
|
if response.get('status') != RESPONSE_OK:
|
|
return None
|
|
tracks = response.get('searchResult3').get('song')
|
|
if tracks is not None:
|
|
return [self.raw_song_to_track(track) for track in tracks]
|
|
return None
|
|
|
|
def find_albums_by_name(self, album_name):
|
|
response = self.connection.search3(album_name, 0, 0, MAX_SEARCH_RESULTS, 0, 0, 0)
|
|
if response.get('status') != RESPONSE_OK:
|
|
return None
|
|
albums = response.get('searchResult3').get('album')
|
|
if albums is not None:
|
|
return [self.raw_album_to_album(album) for album in albums]
|
|
return None
|
|
|
|
def get_raw_artists(self):
|
|
response = self.connection.getIndexes()
|
|
if response.get('status') != RESPONSE_OK:
|
|
return None
|
|
letters = response.get('indexes').get('index')
|
|
if letters is not None:
|
|
return [artist for letter in letters for artist in letter.get('artist')]
|
|
return None
|
|
|
|
def find_song_by_id(self, song_id):
|
|
response = self.connection.getSong(song_id)
|
|
if response.get('status') != RESPONSE_OK:
|
|
return None
|
|
return self.raw_song_to_track(response.get('song')) if response.get('song') is not None else None
|
|
|
|
def find_album_by_id(self, album_id):
|
|
response = self.connection.getAlbum(album_id)
|
|
if response.get('status') != RESPONSE_OK:
|
|
return None
|
|
return self.raw_album_to_album(response.get('album')) if response.get('album') is not None else None
|
|
|
|
def find_artist_by_id(self, artist_id):
|
|
response = self.connection.getArtist(artist_id)
|
|
if response.get('status') != RESPONSE_OK:
|
|
return None
|
|
return self.raw_artist_to_artist(response.get('artist')) if response.get('artist') is not None else None
|
|
|
|
def get_raw_dir(self, parent_id):
|
|
response = self.connection.getMusicDirectory(parent_id)
|
|
if response.get('status') != RESPONSE_OK:
|
|
return None
|
|
directory = response.get('directory')
|
|
if directory is not None:
|
|
return directory.get('child')
|
|
return None
|
|
|
|
def get_raw_albums(self, artist_id):
|
|
return self.get_raw_dir(artist_id)
|
|
|
|
def get_raw_songs(self, album_id):
|
|
return self.get_raw_dir(album_id)
|
|
|
|
def get_albums_as_refs(self, artist_id):
|
|
return sorted([self.raw_album_to_ref(album) for album in self.get_raw_albums(artist_id)], key=ref_sort_key)
|
|
|
|
def get_albums_as_albums(self, artist_id):
|
|
return sorted([self.raw_album_to_album(album) for album in self.get_raw_albums(artist_id)], key=ref_sort_key)
|
|
|
|
def get_songs_as_refs(self, album_id):
|
|
return sorted([self.raw_song_to_ref(song) for song in self.get_raw_songs(album_id)], key=ref_sort_key)
|
|
|
|
def get_songs_as_tracks(self, album_id):
|
|
return sorted([self.raw_song_to_track(song) for song in self.get_raw_songs(album_id)], key=ref_sort_key)
|
|
|
|
def get_artists_as_refs(self):
|
|
return sorted([self.raw_artist_to_ref(artist) for artist in self.get_raw_artists()], key=ref_sort_key)
|
|
|
|
def get_artists_as_artists(self):
|
|
return sorted([self.raw_artist_to_artist(artist) for artist in self.get_raw_artists()], key=lambda artist:artist.name)
|
|
|
|
def raw_song_to_ref(self, song):
|
|
return Ref(
|
|
name=song.get('title') or UNKNOWN_SONG,
|
|
uri=uri.get_song_uri(song.get('id')),
|
|
type=Ref.TRACK)
|
|
|
|
def raw_song_to_track(self, song):
|
|
album_name = song.get('album')
|
|
album = self.find_albums_by_name(album_name)[0] if album_name is not None else None
|
|
artist_name = song.get('artist')
|
|
artist = self.find_artists_by_name(artist_name)[0] if artist_name is not None else None
|
|
return Track(
|
|
name=song.get('title') or UNKNOWN_SONG,
|
|
uri=uri.get_song_uri(song.get('id')),
|
|
bitrate=song.get('bitRate'),
|
|
track_no=int(song.get('track')) if song.get('track') else None,
|
|
date=str(song.get('year')) or 'none',
|
|
genre=song.get('genre'),
|
|
length=int(song.get('duration')) * 1000 if song.get('duration') else None,
|
|
disc_no=int(song.get('discNumber')) if song.get('discNumber') else None,
|
|
artists=[artist] if artist is not None else None,
|
|
album=album
|
|
)
|
|
def raw_album_to_ref(self, album):
|
|
return Ref(
|
|
name=album.get('title') or album.get('name') or UNKNOWN_ALBUM,
|
|
uri=uri.get_album_uri(album.get('id')),
|
|
type=Ref.ALBUM)
|
|
|
|
def raw_album_to_album(self, album):
|
|
artist_name = album.get('artist')
|
|
artist = self.find_artists_by_name(artist_name)[0] if artist_name is not None else None
|
|
return Album(
|
|
name=album.get('title') or album.get('name') or UNKNOWN_ALBUM,
|
|
uri=uri.get_album_uri(album.get('id')),
|
|
artists=[artist]
|
|
)
|
|
|
|
def raw_artist_to_ref(self, artist):
|
|
return Ref(
|
|
name=artist.get('name') or UNKNOWN_ARTIST,
|
|
uri=uri.get_artist_uri(artist.get('id')),
|
|
type=Ref.ARTIST)
|
|
|
|
def raw_artist_to_artist(self, artist):
|
|
return Artist(
|
|
name=artist.get('name') or UNKNOWN_ARTIST,
|
|
uri=uri.get_artist_uri(artist.get('id'))
|
|
)
|