Coverage for coherence/backends/ampache_storage.py : 22%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
# Licensed under the MIT license # http://opensource.org/licenses/mit-license.php
# Copyright 2008, Frank Scholz <coherence@beebits.net>
m = hashlib.md5() m.update(s) return m.hexdigest()
m = hashlib.sha256() m.update(s) return m.hexdigest() except ImportError: import md5 as oldmd5
def md5(s): m = oldmd5.new() m.update(s) return m.hexdigest()
self.uri = uri _, host_port, path, query, _ = urlsplit(uri) if host_port.find(':') != -1: host, port = tuple(host_port.split(':')) port = int(port) else: host = host_port port = 80
utils.ReverseProxyResource.__init__(self, host, port, '?'.join((path, query)))
container_class=DIDLLite.Container, play_container=False): BackendItem.__init__(self) self.id = id self.parent_id = parent_id self.name = name self.mimetype = 'directory' self.container_class = container_class self.update_id = 0 if children_callback is not None: self.children = children_callback else: self.children = [] self.childCount = None
self.store = store self.play_container = play_container
if self.store is not None: self.get_url = lambda: self.store.urlbase + str(self.id)
self.children.append(child) if self.childCount is None: self.childCount = 0 self.childCount += 1
self.info("container.get_children %r %r", start, end) if (end - start > 250 or end - start == 0): end = start + 250
if callable(self.children): return self.children(start, end - start) else: children = self.children if end == 0: return children[start:] else: return children[start:end]
if self.childCount is None: if callable(self.children): self.childCount = len(self.children()) else: self.childCount = len(self.children) return self.childCount
item = self.container_class(self.id, self.parent_id, self.name) item.childCount = self.get_child_count() # if self.store and self.play_container: # if item.childCount > 0: # d = defer.maybeDeferred(self.get_children, 0, 1)
# def process_result(r,item): # res = DIDLLite.PlayContainerResource( # self.store.server.uuid, # cid=self.get_id(),fid=r[0].get_id()) # item.res.append(res) # return item
# def got_error(f,item): # return item
# d.addCallback(process_result,item) # d.addErrback(got_error,item) # return d return item
return self.name
return self.id
BackendItem.__init__(self) self.store = store self.ampache_id = element.get('id') self.id = 'playlist.%d' % int(element.get('id')) self.title = element.find('name').text self.creator = element.find('owner').text self.tracks = int(element.find('items').text) try: self.cover = element.find('art').text except Exception: self.cover = None
return self.store.ampache_query('playlist_songs', start, end - start, filter=self.ampache_id)
return self.tracks
item = DIDLLite.PlaylistItem(self.id, parent_id, self.title) item.childCount = self.get_child_count() # item.artist = self.artist item.albumArtURI = self.cover return item
return self.id
return self.title
return self.cover
BackendItem.__init__(self) self.store = store self.ampache_id = element.get('id') self.id = 'album.%d' % int(element.get('id')) self.title = element.find('name').text self.artist = element.find('artist').text self.tracks = int(element.find('tracks').text) try: self.cover = element.find('art').text except Exception: self.cover = None
return self.store.ampache_query('album_songs', start, end - start, filter=self.ampache_id)
return self.tracks
item = DIDLLite.MusicAlbum(self.id, parent_id, self.title) item.childCount = self.get_child_count() item.artist = self.artist item.albumArtURI = self.cover # if item.childCount > 0: # d = defer.maybeDeferred(self.get_children, 0, 1)
# def process_result(r,item): # res = DIDLLite.PlayContainerResource( # self.store.server.uuid, # cid=self.get_id(), # fid=r[0].get_id()) # item.res.append(res) # return item
# def got_error(f,item): # return item
# d.addCallback(process_result,item) # d.addErrback(got_error,item) # return d return item
return self.id
return self.title
return self.cover
BackendItem.__init__(self) self.store = store self.ampache_id = element.get('id') self.id = 'artist.%d' % int(element.get('id'))
try: self.count_albums = int(element.find('albums').text) except Exception: self.count_albums = None try: self.count_songs = int(element.find('songs').text) except Exception: self.count_songs = None self.name = element.find('name').text
return self.store.ampache_query('artist_albums', start, end - start, filter=self.ampache_id)
if self.count_albums is not None: return self.count_albums
def got_childs(result): self.count_albums = len(result) return self.count_albums
d = self.get_children() d.addCallback(got_childs) return d
item = DIDLLite.MusicArtist(self.id, parent_id, self.name) return item
return self.id
return self.name
BackendItem.__init__(self) self.store = store self.ampache_id = element.get('id') self.id = 'genre.%d' % int(element.get('id'))
try: self.count_albums = int(element.find('albums').text) except Exception: self.count_albums = None try: self.count_artists = int(element.find('artists').text) except Exception: self.count_artists = None try: self.count_songs = int(element.find('songs').text) except Exception: self.count_songs = None self.name = element.find('name').text
return self.store.ampache_query('genre_songs', start, end - start, filter=self.ampache_id)
if self.count_songs is not None: return self.count_songs
def got_childs(result): self.count_songs = len(result) return self.count_songs
d = self.get_children() d.addCallback(got_childs) return d
item = DIDLLite.Genre(self.id, parent_id, self.name) return item
return self.id
return self.name
BackendItem.__init__(self) self.store = store self.ampache_id = element.get('id') self.id = 'tag.%d' % int(element.get('id'))
try: self.count_albums = int(element.find('albums').text) except Exception: self.count_albums = None try: self.count_artists = int(element.find('artists').text) except Exception: self.count_artists = None try: self.count_songs = int(element.find('songs').text) except Exception: self.count_songs = None self.name = element.find('name').text
return self.store.ampache_query('tag_songs', start, end - start, filter=self.ampache_id)
if self.count_songs is not None: return self.count_songs
def got_childs(result): self.count_songs = len(result) return self.count_songs
d = self.get_children() d.addCallback(got_childs) return d
item = DIDLLite.Genre(self.id, parent_id, self.name) return item
return self.id
return self.name
'%H:%M:%S', time.gmtime(seconds))
except Exception: self.cover = None
except Exception: self.size = 0
self.location = ProxySong(self.url)
self.debug("Track get_item %r @ %r", self.id, self.parent_id)
# create item item = DIDLLite.MusicTrack(self.id, self.parent_id) item.album = self.album
item.artist = self.artist # item.date = item.genre = self.genre item.originalTrackNumber = self.track_nr item.title = self.title
item.albumArtURI = self.cover
# add http resource res = DIDLLite.Resource(self.get_url(), 'http-get:*:%s:*' % self.mimetype) if self.size > 0: res.size = self.size if self.duration > 0: res.duration = str(self.duration) if self.bitrate > 0: res.bitrate = str(self.bitrate) item.res.append(res)
return item
return self.store.urlbase + str(self.id) else:
BackendItem.__init__(self) self.store = store self.id = 'video.%d' % int(element.get('id'))
self.url = element.find('url').text
try: seconds = int(element.find('time').text) hours = seconds / 3600 seconds = seconds - hours * 3600 minutes = seconds / 60 seconds = seconds - minutes * 60 self.duration = ("%d:%02d:%02d") % (hours, minutes, seconds) except Exception: self.duration = 0
self.cover = None
self.title = element.find('title').text
self.mimetype = None try: self.mimetype = element.find('mime').text except Exception: self.mimetype, _ = mimetypes.guess_type(self.url, strict=False) if self.mimetype is None: self.mimetype = "video/avi" try: self.size = int(element.find('size').text) except Exception: self.size = 0
if self.store.proxy: self.location = ProxySong(self.url)
return []
return 0
self.debug("video get_item %r @ %r", self.id, parent_id)
# create item item = DIDLLite.VideoItem(self.id, parent_id) item.title = self.title
item.albumArtURI = self.cover
# add http resource res = DIDLLite.Resource(self.get_url(), 'http-get:*:%s:*' % self.mimetype) if self.size > 0: res.size = self.size if self.duration > 0: res.duration = str(self.duration) item.res.append(res)
return item
return self.id
return self.title
if self.store.proxy: return self.store.urlbase + str(self.id) else: return self.url
return None
""" this is a backend to the Ampache Media DB
"""
BackendStore.__init__(self, server, **kwargs) self.config = kwargs self.name = kwargs.get('name', 'Ampache') self.key = kwargs.get('password', kwargs.get('key', '')) self.user = kwargs.get('user', None) self.url = kwargs.get('url', 'http://localhost/ampache/server/xml.server.php')
if kwargs.get('proxy', 'no') in [1, 'Yes', 'yes', 'True', 'true']: self.proxy = True else: self.proxy = False
self.update_id = 0 self.token = None
self.songs = 0 self.albums = 0 self.artists = 0
self.api_version = int(kwargs.get('api_version', 350001)) # self.api_version=int(kwargs.get('api_version',340001))
self.get_token()
return "Ampache storage"
self.info("looking for id %r", id) if isinstance(id, str): id = id.split('@', 1)[0] elif isinstance(id, bytes): id = id.decode('utf-8').split('@', 1)[0] if isinstance(id, str) and id.startswith('artist_all_tracks_'): try: return self.containers[id] except Exception: return None item = None try: id = int(id) item = self.containers[id] except ValueError: try: type, id = id.split('.') if type in ['song', 'artist', 'album', 'playlist', 'genre', 'tag', 'video']: item = self.ampache_query(type, filter=str(id)) except ValueError: return None return item
self.info("got_auth_response %r", response) try: response = etree.fromstring(response) except SyntaxError as msg: self.warning('error parsing ampache answer %r', msg) raise SyntaxError('error parsing ampache answer %r' % msg) try: error = response.find('error').text self.warning('error on token request %r', error) raise ValueError(error) except AttributeError: try: self.token = response.find('auth').text self.songs = int(response.find('songs').text) self.albums = int(response.find('albums').text) self.artists = int(response.find('artists').text) try: self.playlists = int(response.find('playlists').text) except Exception: self.playlists = 0 try: self.genres = int(response.find('genres').text) except Exception: self.genres = 0 try: self.tags = int(response.find('tags').text) except Exception: self.tags = 0 try: self.videos = int(response.find('videos').text) except Exception: self.videos = 0 self.info('ampache returned auth token %r', self.token) self.info( 'Songs: %d, Artists: %d, Albums: %d, Playlists %d, ' 'Genres %d, Tags %d, Videos %d', self.songs, self.artists, self.albums, self.playlists, self.genres, self.tags, self.videos)
if not renegotiate: self.containers = {} self.containers[ROOT_CONTAINER_ID] = \ Container(ROOT_CONTAINER_ID, -1, self.name, store=self)
self.wmc_mapping.update( {'4': lambda: self.get_by_id( AUDIO_ALL_CONTAINER_ID), # all tracks '5': lambda: self.get_by_id( AUDIO_GENRE_CONTAINER_ID), # all genres '6': lambda: self.get_by_id( AUDIO_ARTIST_CONTAINER_ID), # all artists '7': lambda: self.get_by_id( AUDIO_ALBUM_CONTAINER_ID), # all albums '13': lambda: self.get_by_id( AUDIO_PLAYLIST_CONTAINER_ID), # all playlists '8': lambda: self.get_by_id( VIDEO_CONTAINER_ID), # all videos })
louie.send('Coherence.UPnP.Backend.init_completed', None, backend=self) except AttributeError: raise ValueError('no authorization token returned')
self.warning('error calling ampache %r', e) if not renegotiate: louie.send('Coherence.UPnP.Backend.init_failed', None, backend=self, msg=e)
""" ask Ampache for the authorization token """ timestamp = int(time.time()) if self.api_version < 350001: passphrase = md5('%d%s' % (timestamp, self.key)) else: passphrase = sha256('%d%s' % (timestamp, sha256(self.key))) request = ''.join( (self.url, '?action=handshake&auth=%s×tamp=%d' % (passphrase, timestamp))) if self.user is not None: request = ''.join((request, '&user=%s' % self.user)) if self.api_version is not None: request = ''.join((request, '&version=%s' % str(self.api_version))) self.info("auth_request %r", request) d = utils.getPage(request) d.addCallback(self.got_auth_response, renegotiate) d.addErrback(self.got_auth_error, renegotiate) return d
self.warning('error calling ampache %r', e) return e
self.info("got a response for %r", query_item) self.debug(response) response = etree.fromstring(response) items = [] try: error = response.find('error') self.warning('error on token request %r %r', error.attrib['code'], error.text) if error.attrib['code'] == '401': # session error, we need to renegotiate our session d = self.get_token(renegotiate=True)
def resend_request(result, old_request): # exchange the auth token in the resending request new_request = old_request.split('&') for part in new_request: if part.startswith('auth='): new_request[new_request.index( part)] = 'auth=%s' % self.token break new_request = '&'.join(new_request) self.info("ampache_query %r", new_request) return utils.getPage(new_request)
d.addCallback(resend_request, request) d.addErrBack(self.got_error) return d raise ValueError(error.text) except AttributeError: if query_item in ('song', 'artist', 'album', 'playlist', 'genre', 'tag', 'video'): q = response.find(query_item) if q is None: return None else: if q.tag in ['song']: return Track(self, q) if q.tag == 'artist': return Artist(self, q) if q.tag in ['album']: return Album(self, q) if q.tag in ['playlist']: return Playlist(self, q) if q.tag in ['genre']: return Genre(self, q) if q.tag in ['tag']: return Tag(self, q) if q.tag in ['video']: return Video(self, q) else: if query_item in ('songs', 'artists', 'albums', 'playlists', 'genres', 'tags', 'videos'): query_item = query_item[:-1] if query_item in ('playlist_songs', 'album_songs', 'genre_songs', 'tag_songs'): query_item = 'song' if query_item in ('artist_albums',): query_item = 'album' for q in response.findall(query_item): if query_item in ('song',): items.append(Track(self, q)) if query_item in ('artist',): items.append(Artist(self, q)) if query_item in ('album',): items.append(Album(self, q)) if query_item in ('playlist',): items.append(Playlist(self, q)) if query_item in ('genre',): items.append(Genre(self, q)) if query_item in ('tag',): items.append(Tag(self, q)) if query_item in ('video',): items.append(Video(self, q)) return items
request = ''.join( (self.url, '?action=%s&auth=%s&offset=%d' % ( item, self.token, start))) if request_count > 0: request = ''.join((request, '&limit=%d' % request_count)) if filter is not None: request = ''.join((request, '&filter=%s' % filter)) self.info("ampache_query %r", request) d = utils.getPage(request) d.addCallback(self.got_response, item, request) d.addErrback(self.got_error) return d
return self.ampache_query('songs', start, request_count)
return self.ampache_query('albums', start, request_count)
return self.ampache_query('artists', start, request_count)
return self.ampache_query('playlists', start, request_count)
return self.ampache_query('genres', start, request_count)
return self.ampache_query('tags', start, request_count)
return self.ampache_query('videos', start, request_count)
if self.server: self.server.connection_manager_server.set_variable( 0, 'SourceProtocolInfo', ['http-get:*:audio/mpeg:*', 'http-get:*:application/ogg:*', 'http-get:*:video/mp4:*', 'http-get:*:video/x-msvideo:*', 'http-get:*:video/avi:*', 'http-get:*:video/quicktime:*', ])
self.containers[AUDIO_ALL_CONTAINER_ID] = \ Container(AUDIO_ALL_CONTAINER_ID, ROOT_CONTAINER_ID, 'All tracks', store=self, children_callback=self.ampache_query_songs, play_container=True) self.containers[AUDIO_ALL_CONTAINER_ID].childCount = self.songs self.containers[ROOT_CONTAINER_ID].add_child( self.containers[AUDIO_ALL_CONTAINER_ID])
self.containers[AUDIO_ALBUM_CONTAINER_ID] = \ Container(AUDIO_ALBUM_CONTAINER_ID, ROOT_CONTAINER_ID, 'Albums', store=self, children_callback=self.ampache_query_albums) self.containers[AUDIO_ALBUM_CONTAINER_ID].childCount = self.albums self.containers[ROOT_CONTAINER_ID].add_child( self.containers[AUDIO_ALBUM_CONTAINER_ID])
self.containers[AUDIO_ARTIST_CONTAINER_ID] = \ Container(AUDIO_ARTIST_CONTAINER_ID, ROOT_CONTAINER_ID, 'Artists', store=self, children_callback=self.ampache_query_artists) self.containers[AUDIO_ARTIST_CONTAINER_ID].childCount = self.artists self.containers[ROOT_CONTAINER_ID].add_child( self.containers[AUDIO_ARTIST_CONTAINER_ID])
self.containers[AUDIO_PLAYLIST_CONTAINER_ID] = \ Container(AUDIO_PLAYLIST_CONTAINER_ID, ROOT_CONTAINER_ID, 'Playlists', store=self, children_callback=self.ampache_query_playlists, container_class=DIDLLite.PlaylistContainer) self.containers[AUDIO_PLAYLIST_CONTAINER_ID].childCount = \ self.playlists self.containers[ROOT_CONTAINER_ID].add_child( self.containers[AUDIO_PLAYLIST_CONTAINER_ID])
self.containers[AUDIO_GENRE_CONTAINER_ID] = \ Container(AUDIO_GENRE_CONTAINER_ID, ROOT_CONTAINER_ID, 'Genres', store=self, children_callback=self.ampache_query_genres) self.containers[AUDIO_GENRE_CONTAINER_ID].childCount = self.genres self.containers[ROOT_CONTAINER_ID].add_child( self.containers[AUDIO_GENRE_CONTAINER_ID])
self.containers[AUDIO_TAG_CONTAINER_ID] = \ Container(AUDIO_TAG_CONTAINER_ID, ROOT_CONTAINER_ID, 'Tags', store=self, children_callback=self.ampache_query_tags) self.containers[AUDIO_TAG_CONTAINER_ID].childCount = self.tags self.containers[ROOT_CONTAINER_ID].add_child( self.containers[AUDIO_TAG_CONTAINER_ID])
self.containers[VIDEO_CONTAINER_ID] = \ Container(VIDEO_CONTAINER_ID, ROOT_CONTAINER_ID, 'Videos', store=self, children_callback=self.ampache_query_videos) self.containers[VIDEO_CONTAINER_ID].childCount = self.videos self.containers[ROOT_CONTAINER_ID].add_child( self.containers[VIDEO_CONTAINER_ID])
try: ObjectID = kwargs['ObjectID'] except Exception as e: self.debug( "hmm, a Browse action and no ObjectID argument? " "An XBox maybe? [ERROR: %r]" % (e,)) try: ObjectID = kwargs['ContainerID'] except Exception: ObjectID = 0 BrowseFlag = kwargs['BrowseFlag'] Filter = kwargs['Filter'] StartingIndex = int(kwargs['StartingIndex']) RequestedCount = int(kwargs['RequestedCount']) SortCriteria = kwargs['SortCriteria'] parent_container = None requested_id = None
if BrowseFlag == 'BrowseDirectChildren': parent_container = str(ObjectID) else: requested_id = str(ObjectID)
self.info("upnp_Browse request %r %r %r %r", ObjectID, BrowseFlag, StartingIndex, RequestedCount)
didl = DIDLLite.DIDLElement( upnp_client=kwargs.get('X_UPnPClient', ''), requested_id=requested_id, parent_container=parent_container)
def build_response(tm): num_ret = didl.numItems() # if int(kwargs['RequestedCount']) != 0 and \ # num_ret != int(kwargs['RequestedCount']): # num_ret = 0 # if RequestedCount == 0 and tm-StartingIndex != num_ret: # num_ret = 0 r = {'Result': didl.toString(), 'TotalMatches': tm, 'NumberReturned': num_ret}
self.info("upnp_Browse response %r %r", num_ret, tm)
if hasattr(item, 'update_id'): r['UpdateID'] = item.update_id elif hasattr(self, 'update_id'): r['UpdateID'] = self.update_id # FIXME else: r['UpdateID'] = 0
return r
def got_error(r): return r
def process_result(result, found_item): if result is None: result = [] if BrowseFlag == 'BrowseDirectChildren': lc = []
def process_items(result, tm): if result is None: result = [] for i in result: if i[0]: didl.addItem(i[1])
return build_response(tm)
for it in result: md = defer.maybeDeferred(it.get_item) lc.append(md)
def got_child_count(count): dl = defer.DeferredList(lc) dl.addCallback(process_items, count) return dl
md = defer.maybeDeferred(found_item.get_child_count) md.addCallback(got_child_count)
return md
else: didl.addItem(result) total = 1
return build_response(total)
total = 0 items = []
wmc_mapping = getattr(self, "wmc_mapping", None) if (kwargs.get('X_UPnPClient', '') == 'XBox' and wmc_mapping is not None and ObjectID in wmc_mapping): """ fake a Windows Media Connect Server """ root_id = wmc_mapping[ObjectID] if callable(root_id): item = root_id() if item is not None: if isinstance(item, list): total = len(item) if int(RequestedCount) == 0: items = item[StartingIndex:] else: items = item[StartingIndex: StartingIndex + RequestedCount] else: d = defer.maybeDeferred(item.get_children, StartingIndex, StartingIndex + RequestedCount) d.addCallback(process_result) d.addErrback(got_error) return d
for i in items: didl.addItem(i.get_item())
return build_response(total)
root_id = ObjectID
item = self.get_by_id(root_id) if item is None: return failure.Failure(errorCode(701))
def proceed(result): if BrowseFlag == 'BrowseDirectChildren': md = defer.maybeDeferred(result.get_children, StartingIndex, StartingIndex + RequestedCount) else: md = defer.maybeDeferred(result.get_item)
md.addCallback(process_result, result) md.addErrback(got_error) return md
if isinstance(item, defer.Deferred): item.addCallback(proceed) return item else: return proceed(item)
from coherence.base import Coherence
def main(): def got_result(result): print("got_result")
def call_browse(ObjectID=0, StartingIndex=0, RequestedCount=0): r = f.backend.upnp_Browse(BrowseFlag='BrowseDirectChildren', RequestedCount=RequestedCount, StartingIndex=StartingIndex, ObjectID=ObjectID, SortCriteria='*', Filter='') r.addCallback(got_result) r.addErrback(got_result)
def call_test(start, count): r = f.backend.ampache_query_artists(start, count) r.addCallback(got_result) r.addErrback(got_result)
config = {} config['logmode'] = 'warning' c = Coherence(config) f = c.add_plugin('AmpacheStore', url='http://localhost/ampache/server/xml.server.php', key='password', user=None) reactor.callLater(3, call_browse, 0, 0, 0) # reactor.callLater(3, call_browse, AUDIO_ALL_CONTAINER_ID, 0, 0) # reactor.callLater(3, call_browse, AUDIO_ARTIST_CONTAINER_ID, 0, 10) # reactor.callLater(3, call_browse, AUDIO_ALBUM_CONTAINER_ID, 0, 10) # reactor.callLater(3, call_test, 0, 10)
reactor.callWhenRunning(main) reactor.run() |