|
|
|
@ -198,7 +198,7 @@ class PysonicApi(object):
|
|
|
|
|
index = response.add_child("index", _parent="indexes", name=letter.upper()) |
|
|
|
|
for artist in artists: |
|
|
|
|
if artist["name"][0].lower() in letter: |
|
|
|
|
response.add_child("artist", _real_parent=index, id=artist["id"], name=artist["name"]) |
|
|
|
|
response.add_child("artist", _real_parent=index, id=artist["dir"], name=artist["name"]) |
|
|
|
|
return response |
|
|
|
|
|
|
|
|
|
@cherrypy.expose |
|
|
|
@ -245,33 +245,54 @@ class PysonicApi(object):
|
|
|
|
|
""" |
|
|
|
|
List an artist dir |
|
|
|
|
""" |
|
|
|
|
artist_id = int(id) |
|
|
|
|
dir_id = int(id) |
|
|
|
|
|
|
|
|
|
cherrypy.response.headers['Content-Type'] = 'text/xml; charset=utf-8' |
|
|
|
|
|
|
|
|
|
response = ApiResponse() |
|
|
|
|
response.add_child("directory") |
|
|
|
|
|
|
|
|
|
artist = self.library.get_artists(id=artist_id)[0] |
|
|
|
|
children = self.library.get_albums(artist=artist_id) |
|
|
|
|
response.set_attrs(_path="directory", name=artist['name'], id=artist['id'], |
|
|
|
|
parent=artist['libraryid'], playCount=10) |
|
|
|
|
dirtype, dirinfo, entity = self.library.db.get_musicdir(dirid=dir_id) |
|
|
|
|
|
|
|
|
|
for item in children: |
|
|
|
|
from pprint import pprint |
|
|
|
|
pprint(dirinfo) |
|
|
|
|
pprint(entity) |
|
|
|
|
|
|
|
|
|
response.set_attrs(_path="directory", name=entity['name'], id=entity['id'], |
|
|
|
|
parent=dirinfo['parent'], playCount=420) |
|
|
|
|
|
|
|
|
|
for childtype, child in entity["children"]: |
|
|
|
|
# omit not dirs and media in browser |
|
|
|
|
# if not item["isdir"] and item["type"] not in MUSIC_TYPES: |
|
|
|
|
# continue |
|
|
|
|
# item_meta = item['metadata'] |
|
|
|
|
moreargs = {} |
|
|
|
|
if childtype == "album": |
|
|
|
|
moreargs.update(name=child["name"], |
|
|
|
|
isDir="true", # TODO song files in artist dir |
|
|
|
|
parent=entity["id"], |
|
|
|
|
coverArt=child["coverid"], |
|
|
|
|
id=child["dir"]) |
|
|
|
|
# album=item["name"], |
|
|
|
|
# title=item["name"], # TODO dupe? |
|
|
|
|
# artist=artist["name"], |
|
|
|
|
# coverArt=item["coverid"], |
|
|
|
|
elif childtype == "song": |
|
|
|
|
moreargs.update(name=child["title"], |
|
|
|
|
artist=child["_artist"]["name"], |
|
|
|
|
contentType=child["format"], |
|
|
|
|
coverArt=entity["coverid"], |
|
|
|
|
id=child["id"], # this is probably fucked ? |
|
|
|
|
duration=child["length"], |
|
|
|
|
isDir="false", |
|
|
|
|
parent=entity["dir"], |
|
|
|
|
# title=xxx |
|
|
|
|
) |
|
|
|
|
# duration="230" size="8409237" suffix="mp3" track="2" year="2005"/> |
|
|
|
|
response.add_child("child", _parent="directory", |
|
|
|
|
album=item["name"], |
|
|
|
|
title=item["name"], # TODO dupe? |
|
|
|
|
artist=artist["name"], |
|
|
|
|
coverArt=item["coverid"], |
|
|
|
|
id=item["id"], |
|
|
|
|
isDir="false", # TODO song files in artist dir |
|
|
|
|
parent=artist["id"], |
|
|
|
|
size="4096", |
|
|
|
|
type="music") |
|
|
|
|
type="music", |
|
|
|
|
**moreargs) |
|
|
|
|
|
|
|
|
|
return response |
|
|
|
|
|
|
|
|
@ -284,11 +305,7 @@ class PysonicApi(object):
|
|
|
|
|
:param directory: |
|
|
|
|
:param dir_meta: |
|
|
|
|
""" |
|
|
|
|
print("\n\n\n") |
|
|
|
|
print(item) |
|
|
|
|
print(item_meta) |
|
|
|
|
print(directory) |
|
|
|
|
print(dir_meta) |
|
|
|
|
raise Exception("stop using this") |
|
|
|
|
child = dict(id=item["id"], |
|
|
|
|
parent=item["id"], |
|
|
|
|
isDir="true" if "file" not in item else "false", |
|
|
|
@ -328,15 +345,19 @@ class PysonicApi(object):
|
|
|
|
|
def stream_view(self, id, maxBitRate="256", **kwargs): |
|
|
|
|
maxBitRate = int(maxBitRate) |
|
|
|
|
assert maxBitRate >= 32 and maxBitRate <= 320 |
|
|
|
|
fpath = self.library.get_filepath(id) |
|
|
|
|
meta = self.library.get_file_metadata(id) |
|
|
|
|
to_bitrate = min(maxBitRate, self.options.max_bitrate, meta.get("media_kbitrate", 320)) |
|
|
|
|
song = self.library.get_song(id) |
|
|
|
|
fpath = "library/" + song["file"] |
|
|
|
|
# import pdb |
|
|
|
|
# from pprint import pprint |
|
|
|
|
# pdb.set_trace() |
|
|
|
|
# meta = self.library.get_file_metadata(id) |
|
|
|
|
to_bitrate = min(maxBitRate, self.options.max_bitrate, song.get("bitrate", 320 * 1024) / 1024) |
|
|
|
|
cherrypy.response.headers['Content-Type'] = 'audio/mpeg' |
|
|
|
|
if "media_length" in meta: |
|
|
|
|
cherrypy.response.headers['X-Content-Duration'] = str(int(meta['media_length'])) |
|
|
|
|
#if "media_length" in meta: |
|
|
|
|
# cherrypy.response.headers['X-Content-Duration'] = str(int(meta['media_length'])) |
|
|
|
|
cherrypy.response.headers['X-Content-Kbitrate'] = str(to_bitrate) |
|
|
|
|
if (self.options.skip_transcode or meta.get("media_kbitrate", -1) == to_bitrate) \ |
|
|
|
|
and meta["type"] == "audio/mpeg": |
|
|
|
|
if (self.options.skip_transcode or song.get("bitrate", -1024) / 1024 == to_bitrate) \ |
|
|
|
|
and format["type"] == "audio/mpeg": |
|
|
|
|
def content(): |
|
|
|
|
with open(fpath, "rb") as f: |
|
|
|
|
while True: |
|
|
|
@ -346,10 +367,10 @@ class PysonicApi(object):
|
|
|
|
|
yield data |
|
|
|
|
return content() |
|
|
|
|
else: |
|
|
|
|
transcode_meta = "transcoded_{}_size".format(to_bitrate) |
|
|
|
|
if transcode_meta in meta: |
|
|
|
|
cherrypy.response.headers['Content-Length'] = str(int(meta[transcode_meta])) |
|
|
|
|
|
|
|
|
|
# transcode_meta = "transcoded_{}_size".format(to_bitrate) |
|
|
|
|
# if transcode_meta in meta: |
|
|
|
|
# cherrypy.response.headers['Content-Length'] = str(int(meta[transcode_meta])) |
|
|
|
|
print(fpath) |
|
|
|
|
transcode_args = ["ffmpeg", "-i", fpath, "-map", "0:0", "-b:a", |
|
|
|
|
"{}k".format(to_bitrate), |
|
|
|
|
"-v", "0", "-f", "mp3", "-"] |
|
|
|
@ -359,13 +380,13 @@ class PysonicApi(object):
|
|
|
|
|
|
|
|
|
|
def content(proc): |
|
|
|
|
length = 0 |
|
|
|
|
completed = False |
|
|
|
|
# completed = False |
|
|
|
|
start = time() |
|
|
|
|
try: |
|
|
|
|
while True: |
|
|
|
|
data = proc.stdout.read(16 * 1024) |
|
|
|
|
if not data: |
|
|
|
|
completed = True |
|
|
|
|
# completed = True |
|
|
|
|
break |
|
|
|
|
yield data |
|
|
|
|
length += len(data) |
|
|
|
@ -373,8 +394,8 @@ class PysonicApi(object):
|
|
|
|
|
proc.poll() |
|
|
|
|
if proc.returncode is None or proc.returncode == 0: |
|
|
|
|
logging.warning("transcoded {} in {}s".format(id, int(time() - start))) |
|
|
|
|
if completed: |
|
|
|
|
self.library.report_transcode(id, to_bitrate, length) |
|
|
|
|
# if completed: |
|
|
|
|
# self.library.report_transcode(id, to_bitrate, length) |
|
|
|
|
else: |
|
|
|
|
logging.error("transcode of {} exited with code {} after {}s".format(id, proc.returncode, |
|
|
|
|
int(time() - start))) |
|
|
|
@ -394,7 +415,8 @@ class PysonicApi(object):
|
|
|
|
|
|
|
|
|
|
@cherrypy.expose |
|
|
|
|
def getCoverArt_view(self, id, **kwargs): |
|
|
|
|
fpath = self.library.get_filepath(id) |
|
|
|
|
cover = self.library.get_cover(id) |
|
|
|
|
fpath = "library/" + cover["path"] |
|
|
|
|
type2ct = { |
|
|
|
|
'jpg': 'image/jpeg', |
|
|
|
|
'png': 'image/png', |
|
|
|
|