Begin abstracting api responses

This commit is contained in:
dave 2017-08-19 23:01:47 -07:00
parent 7c9b1d7869
commit 73f0ff1201
1 changed files with 79 additions and 23 deletions

View File

@ -1,11 +1,11 @@
import sys
import json
import logging
import cherrypy
import subprocess
from time import time
from random import shuffle
from threading import Thread
from time import sleep
import cherrypy
from collections import defaultdict
from bs4 import BeautifulSoup
from pysonic.library import LETTER_GROUPS
from pysonic.types import MUSIC_TYPES
@ -14,6 +14,65 @@ from pysonic.types import MUSIC_TYPES
logging = logging.getLogger("api")
class ApiResponse(object):
def __init__(self, status="ok", version="1.15.0", top=None, **kwargs):
self.status = status
self.version = version
self.data = {}
self.top = top
if self.top:
self.data[self.top] = kwargs ## kwargs unused TODO
def add_child(self, _type, **kwargs):
if not self.top:
raise Exception("You can't do this?")
if _type not in self.data[self.top]:
self.data[self.top][_type] = []
self.data[self.top][_type].append(kwargs)
def render_json(self):
return json.dumps({"subsonic-response": dict(status=self.status, version="1.15.0", **self.data)}, indent=4)
def render_xml(self):
doc = BeautifulSoup('', features='lxml-xml')
root = doc.new_tag("subsonic-response", xmlns="http://subsonic.org/restapi",
status=self.status,
version=self.version)
doc.append(root)
if self.top:
top = doc.new_tag(self.top)
root.append(top)
# TODO top_attrs ?
for top_child_type, top_child_instances in self.data[self.top].items():
for top_child_attrs in top_child_instances:
child = doc.new_tag(top_child_type)
child.attrs.update(top_child_attrs)
top.append(child)
albumlist = doc.new_tag("albumList")
doc.append(albumlist)
return doc.prettify()
response_formats = defaultdict(lambda: "render_xml")
response_formats["json"] = "render_json"
response_headers = defaultdict(lambda: "text/xml; charset=utf-8")
response_headers["json"] = "text/json" #TODO is this right?
def formatresponse(func):
"""
Decorator for rendering ApiResponse responses
"""
def wrapper(*args, **kwargs):
response = func(*args, **kwargs)
cherrypy.response.headers['Content-Type'] = response_headers[kwargs.get("f", "xml")]
return getattr(response, response_formats[kwargs.get("f", "xml")])()
return wrapper
class PysonicApi(object):
def __init__(self, db, library, options):
self.db = db
@ -104,6 +163,7 @@ class PysonicApi(object):
print("TODO save playlist with items {} current {} position {}".format(id, current, position))
@cherrypy.expose
@formatresponse
def getAlbumList_view(self, type, size=50, offset=0, **kwargs):
albums = self.library.get_albums()
if type == "random":
@ -114,15 +174,11 @@ class PysonicApi(object):
raise NotImplemented()
albumset = albums[0 + int(offset):int(size) + int(offset)]
cherrypy.response.headers['Content-Type'] = 'text/xml; charset=utf-8'
doc, root = self.response()
albumlist = doc.new_tag("albumList")
doc.append(albumlist)
response = ApiResponse(top="albumList")
for album in albumset:
album_meta = album['metadata']
tag = doc.new_tag("album",
id=album["id"],
album_kw = dict(id=album["id"],
parent=album["parent"],
isDir="true" if album['isdir'] else "false",
title=album_meta.get("id3_title", album["name"]), #TODO these cant be blank or dsub gets mad
@ -131,14 +187,14 @@ class PysonicApi(object):
# X year="2014"
# X coverArt="3228"
# playCount="0"
# created="2016-05-08T05:31:31.000Z"/>
# created="2016-05-08T05:31:31.000Z"/>)
)
if 'cover' in album_meta:
tag.attrs["coverArt"] = album_meta["cover"]
album_kw["coverArt"] = album_meta["cover"]
if 'id3_year' in album_meta:
tag.attrs["year"] = album_meta['id3_year']
albumlist.append(tag)
yield doc.prettify()
album_kw["year"] = album_meta['id3_year']
response.add_child("album", **album_kw)
return response
@cherrypy.expose
def getMusicDirectory_view(self, id, **kwargs):