implement album listings

This commit is contained in:
dave 2018-04-04 21:41:17 -07:00
parent 33e501928e
commit ac9b3620e9
3 changed files with 61 additions and 85 deletions

View File

@ -202,41 +202,39 @@ class PysonicApi(object):
response.add_child("artist", _real_parent=index, id=artist["dir"], name=artist["name"]) response.add_child("artist", _real_parent=index, id=artist["dir"], name=artist["name"])
return response return response
@cherrypy.expose
def savePlayQueue_view(self, id, current, position, **kwargs):
print("TODO save playlist with items {} current {} position {}".format(id, current, position))
@cherrypy.expose @cherrypy.expose
@formatresponse @formatresponse
def getAlbumList_view(self, type, size=50, offset=0, **kwargs): def getAlbumList_view(self, type, size=50, offset=0, **kwargs):
albums = self.library.get_albums() qargs = {}
if type == "random": if type == "random":
shuffle(albums) qargs.update(sortby="random")
elif type == "alphabeticalByName": elif type == "alphabeticalByName":
albums.sort(key=lambda item: item.get("id3_album", item["album"] if item["album"] else "zzzzzUnsortable")) qargs.update(sortby="name", order="asc")
elif type == "newest":
qargs.update(sortby="added", order="desc")
else: else:
raise NotImplemented() raise NotImplemented()
albumset = albums[0 + int(offset):int(size) + int(offset)]
qargs.update(limit=(offset, size))
albums = self.library.get_albums(**qargs)
response = ApiResponse() response = ApiResponse()
response.add_child("albumList") response.add_child("albumList")
for album in albumset: for album in albums:
album_meta = album['metadata'] album_kw = dict(id=album["dir"],
album_kw = dict(id=album["id"], parent=album["artistdir"],
parent=album["parent"], isDir="true",
isDir="true" if album['isdir'] else "false", title=album["name"],
title=album_meta.get("id3_title", album["name"]), #TODO these cant be blank or dsub gets mad album=album["name"],
album=album_meta.get("id3_album", album["album"]), artist=album["artistname"],
artist=album_meta.get("id3_artist", album["artist"]), coverArt=album["coverid"]
#year=TODO
# playCount="0" # playCount="0"
# created="2016-05-08T05:31:31.000Z"/>) # created="2016-05-08T05:31:31.000Z"/>)
) )
if 'cover' in album_meta:
album_kw["coverArt"] = album_meta["cover"]
if 'id3_year' in album_meta:
album_kw["year"] = album_meta['id3_year']
response.add_child("album", _parent="albumList", **album_kw) response.add_child("album", _parent="albumList", **album_kw)
return response return response
@ -247,18 +245,10 @@ class PysonicApi(object):
List an artist dir List an artist dir
""" """
dir_id = int(id) dir_id = int(id)
dirtype, dirinfo, entity = self.library.db.get_musicdir(dirid=dir_id)
cherrypy.response.headers['Content-Type'] = 'text/xml; charset=utf-8'
response = ApiResponse() response = ApiResponse()
response.add_child("directory") response.add_child("directory")
dirtype, dirinfo, entity = self.library.db.get_musicdir(dirid=dir_id)
from pprint import pprint
pprint(dirinfo)
pprint(entity)
response.set_attrs(_path="directory", name=entity['name'], id=entity['id'], response.set_attrs(_path="directory", name=entity['name'], id=entity['id'],
parent=dirinfo['parent'], playCount=420) parent=dirinfo['parent'], playCount=420)
@ -297,53 +287,9 @@ class PysonicApi(object):
type="music", type="music",
**moreargs) **moreargs)
cherrypy.response.headers['Content-Type'] = 'text/xml; charset=utf-8'
return response return response
def render_node(self, item, item_meta, directory, dir_meta):
"""
Given a node and it's parent directory, and meta, return a dict with the keys formatted how the subsonic clients
expect them to be
:param item:
:param item_meta:
:param directory:
:param dir_meta:
"""
raise Exception("stop using this")
child = dict(id=item["id"],
parent=item["id"],
isDir="true" if "file" not in item else "false",
title=item_meta.get("id3_title", item["name"]),
album=item_meta.get("id3_album", item["album"]),
artist=item_meta.get("id3_artist", item["artist"]),
# playCount="5",
# created="2016-04-25T07:31:33.000Z"
# genre="Other",
# path="Cosmic Gate/Sign Of The Times/03 Flatline (featuring Kyler England).mp3"
type="music")
if 'kbitrate' in item_meta:
child["bitrate"] = item_meta["kbitrate"]
if item["size"] != -1:
child["size"] = item["size"]
if "media_length" in item_meta:
child["duration"] = item_meta["media_length"]
if "albumId" in directory:
child["albumId"] = directory["id"]
if "artistId" in directory:
child["artistId"] = directory["parent"]
if "." in item["name"]:
child["suffix"] = item["name"].split(".")[-1]
if item["type"]:
child["contentType"] = item["type"]
if 'cover' in item_meta:
child["coverArt"] = item_meta["cover"]
elif 'cover' in dir_meta:
child["coverArt"] = dir_meta["cover"]
if 'track' in item_meta:
child["track"] = item_meta['track']
if 'id3_year' in item_meta:
child["year"] = item_meta['id3_year']
return child
@cherrypy.expose @cherrypy.expose
def stream_view(self, id, maxBitRate="256", **kwargs): def stream_view(self, id, maxBitRate="256", **kwargs):
maxBitRate = int(maxBitRate) maxBitRate = int(maxBitRate)
@ -372,7 +318,6 @@ class PysonicApi(object):
# transcode_meta = "transcoded_{}_size".format(to_bitrate) # transcode_meta = "transcoded_{}_size".format(to_bitrate)
# if transcode_meta in meta: # if transcode_meta in meta:
# cherrypy.response.headers['Content-Length'] = str(int(meta[transcode_meta])) # cherrypy.response.headers['Content-Length'] = str(int(meta[transcode_meta]))
print(fpath)
transcode_args = ["ffmpeg", "-i", fpath, "-map", "0:0", "-b:a", transcode_args = ["ffmpeg", "-i", fpath, "-map", "0:0", "-b:a",
"{}k".format(to_bitrate), "{}k".format(to_bitrate),
"-v", "0", "-f", "mp3", "-"] "-v", "0", "-f", "mp3", "-"]
@ -437,7 +382,6 @@ class PysonicApi(object):
yield data yield data
logging.info("\nSent {} bytes for {}".format(total, fpath)) logging.info("\nSent {} bytes for {}".format(total, fpath))
return content() return content()
getCoverArt_view._cp_config = {'response.stream': True} getCoverArt_view._cp_config = {'response.stream': True}
@cherrypy.expose @cherrypy.expose
@ -558,7 +502,7 @@ class PysonicApi(object):
:param submission: True if end of song reached. False on start of track. :param submission: True if end of song reached. False on start of track.
""" """
submission = True if submission == "true" else False submission = True if submission == "true" else False
# TODO save played track stats # TODO save played track stats and/or do last.fm bullshit
return ApiResponse() return ApiResponse()
@cherrypy.expose @cherrypy.expose
@ -606,3 +550,9 @@ class PysonicApi(object):
def setRating_view(self, id, rating): def setRating_view(self, id, rating):
# rating is 1-5 # rating is 1-5
pass pass
@cherrypy.expose
def savePlayQueue_view(self, id, current, position, **kwargs):
print("TODO save playlist with items {} current {} position {}".format(id, current, position))
# TODO save playlist with items ['378', '386', '384', '380', '383'] current 383 position 4471
# id entries are strings!

View File

@ -73,6 +73,7 @@ class PysonicDatabase(object):
'coverid' INTEGER, 'coverid' INTEGER,
'dir' INTEGER, 'dir' INTEGER,
'name' TEXT, 'name' TEXT,
'added' INTEGER NOT NULL DEFAULT -1,
UNIQUE (artistid, dir));""", UNIQUE (artistid, dir));""",
"""CREATE TABLE 'songs' ( """CREATE TABLE 'songs' (
'id' INTEGER PRIMARY KEY AUTOINCREMENT, 'id' INTEGER PRIMARY KEY AUTOINCREMENT,
@ -181,11 +182,31 @@ class PysonicDatabase(object):
return artists return artists
@readcursor @readcursor
def get_albums(self, cursor, id=None, artist=None, sortby=None, order=None): def get_albums(self, cursor, id=None, artist=None, sortby=None, order=None, limit=None):
assert order in ["asc", "desc", None] """
:param limit: int or tuple of int, int. translates directly to sql logic.
"""
if order:
order = {"asc": "ASC", "desc": "DESC"}[order]
if sortby and sortby == "random":
sortby = "RANDOM()"
albums = [] albums = []
q = "SELECT * FROM albums" q = """
SELECT
alb.*,
art.name as artistname,
dirs.parent as artistdir
FROM albums as alb
INNER JOIN artists as art
on alb.artistid = art.id
INNER JOIN dirs
on dirs.id = alb.dir
"""
#q = "SELECT * FROM albums"
params = [] params = []
conditions = [] conditions = []
@ -199,7 +220,14 @@ class PysonicDatabase(object):
q += " WHERE " + " AND ".join(conditions) q += " WHERE " + " AND ".join(conditions)
if sortby: if sortby:
q += " ORDER BY {} {}".format(sortby, order.upper() if order else "ASC") q += " ORDER BY {}".format(sortby)
if order:
q += " {}".format(order)
if limit:
q += " LIMIT {}".format(limit) if isinstance(limit, int) \
else " LIMIT {}, {}".format(*limit)
cursor.execute(q, params) cursor.execute(q, params)
for row in cursor: for row in cursor:
albums.append(row) albums.append(row)
@ -253,8 +281,6 @@ class PysonicDatabase(object):
if limit: if limit:
q += " LIMIT {}".format(limit) # TODO support limit pagination q += " LIMIT {}".format(limit) # TODO support limit pagination
print(q)
cursor.execute(q, params) cursor.execute(q, params)
for row in cursor: for row in cursor:
songs.append(row) songs.append(row)

View File

@ -191,8 +191,8 @@ class PysonicFilesystemScanner(object):
if row: if row:
album_id = row['id'] album_id = row['id']
else: else:
cursor.execute("INSERT INTO albums (artistid, dir, name) VALUES (?, ?, ?)", cursor.execute("INSERT INTO albums (artistid, dir, name, added) VALUES (?, ?, ?, ?)",
(artist_id, album_dirid, dirnames[-1])) (artist_id, album_dirid, dirnames[-1], int(time())))
album_id = cursor.lastrowid album_id = cursor.lastrowid
return album_id, album_dirid return album_id, album_dirid