add id3 scanner and support more apis

This commit is contained in:
dave 2017-08-13 23:54:37 -07:00
parent 99aae4165f
commit 2b87e1d2ec
4 changed files with 124 additions and 14 deletions

View File

@ -3,6 +3,7 @@ import logging
import cherrypy import cherrypy
import subprocess import subprocess
from time import time from time import time
from random import shuffle
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
from pysonic.library import LETTER_GROUPS from pysonic.library import LETTER_GROUPS
from pysonic.types import MUSIC_TYPES from pysonic.types import MUSIC_TYPES
@ -85,6 +86,58 @@ class PysonicApi(object):
index.append(artist_tag) index.append(artist_tag)
yield doc.prettify() yield doc.prettify()
@cherrypy.expose
def savePlayQueue_view(self, id, current, position, **kwargs):
# /rest/savePlayQueue.view?
# u=dave&
# s=h7vcg97gm2vbb7m4133pavs1ot&
# t=355f45124d9d3a75fe681c11d94ed066&
# v=1.2.0&
# c=DSub&
# id=296&
# id=289&
# id=292&id=287&id=288&id=290&id=293&id=294&id=297&id=298&id=291&
# current=297&
# position=0
print("TODO save playlist with items {} current {} position {}".format(id, current, position))
@cherrypy.expose
def getAlbumList_view(self, type, size=50, offset=0, **kwargs):
albums = self.library.get_albums()
if type == "random":
shuffle(albums)
elif type == "alphabeticalByName":
albums.sort(key=lambda item: item.get("id3_album", item["album"]))
else:
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)
for album in albumset:
album_meta = self.library.db.decode_metadata(album['metadata'])
tag = doc.new_tag("album",
id=album["id"],
parent=album["parent"],
isDir="true" if album['isdir'] else "false",
title=album_meta.get("id3_title", album["name"]),
album=album_meta.get("id3_album", album["album"]),
artist=album_meta.get("id3_artist", album["artist"]),
# X year="2014"
# X coverArt="3228"
# playCount="0"
# created="2016-05-08T05:31:31.000Z"/>
)
if 'cover' in album_meta:
tag.attrs["coverArt"] = album_meta["cover"]
if 'id3_year' in album_meta:
tag.attrs["year"] = album_meta['id3_year']
albumlist.append(tag)
yield doc.prettify()
@cherrypy.expose @cherrypy.expose
def getMusicDirectory_view(self, id, **kwargs): def getMusicDirectory_view(self, id, **kwargs):
""" """
@ -108,34 +161,41 @@ class PysonicApi(object):
# omit not dirs and media in browser # omit not dirs and media in browser
if not item["isdir"] and item["type"] not in MUSIC_TYPES: if not item["isdir"] and item["type"] not in MUSIC_TYPES:
continue continue
item_meta = self.db.decode_metadata(item['metadata'])
child = doc.new_tag("child", child = doc.new_tag("child",
id=item["id"], id=item["id"],
parent=directory["id"], parent=directory["id"],
isDir="true" if item['isdir'] else "false", isDir="true" if item['isdir'] else "false",
title=item["name"], title=item_meta.get("id3_title", item["name"]),
album=item["name"], album=item_meta.get("id3_album", item["album"]),
artist=directory["name"], artist=item_meta.get("id3_artist", item["artist"]),
# playCount="5", # playCount="5",
# created="2016-04-25T07:31:33.000Z" # created="2016-04-25T07:31:33.000Z"
# track="3", # X track="3",
# year="2012", # X year="2012",
# X coverArt="12835",
# X contentType="audio/mpeg"
# X suffix="mp3"
# genre="Other", # genre="Other",
# coverArt="12835",
# contentType="audio/mpeg"
# suffix="mp3"
# size="15838864" # size="15838864"
# duration="395" # duration="395"
# bitRate="320" # bitRate="320"
# path="Cosmic Gate/Sign Of The Times/03 Flatline (featuring Kyler England).mp3" # path="Cosmic Gate/Sign Of The Times/03 Flatline (featuring Kyler England).mp3"
# albumId="933" albumId=directory["id"],
# artistId="353" artistId=directory["parent"],
# type="music"/> type="music")
) if "." in item["name"]:
item_meta = self.db.decode_metadata(item['metadata']) child.attrs["suffix"] = item["name"].split(".")[-1]
if item_meta["type"]:
child.attrs["contentType"] = item_meta["type"]
if 'cover' in item_meta: if 'cover' in item_meta:
child.attrs["coverArt"] = item_meta["cover"] child.attrs["coverArt"] = item_meta["cover"]
elif 'cover' in dir_meta: elif 'cover' in dir_meta:
child.attrs["coverArt"] = dir_meta["cover"] child.attrs["coverArt"] = dir_meta["cover"]
if 'track' in item_meta:
child.attrs["track"] = item_meta['track']
if 'id3_year' in item_meta:
child.attrs["year"] = item_meta['id3_year']
dirtag.append(child) dirtag.append(child)
yield doc.prettify() yield doc.prettify()

View File

@ -37,6 +37,7 @@ def main():
logging.warning("Libraries: {}".format([i["name"] for i in library.get_libraries()])) logging.warning("Libraries: {}".format([i["name"] for i in library.get_libraries()]))
logging.warning("Artists: {}".format([i["name"] for i in library.get_artists()])) logging.warning("Artists: {}".format([i["name"] for i in library.get_artists()]))
logging.warning("Albums: {}".format(len(library.get_albums())))
cherrypy.tree.mount(PysonicApi(db, library, args), '/rest/', {'/': {}}) cherrypy.tree.mount(PysonicApi(db, library, args), '/rest/', {'/': {}})
cherrypy.config.update({ cherrypy.config.update({

View File

@ -68,6 +68,10 @@ class PysonicLibrary(object):
def get_dir_children(self, dirid): def get_dir_children(self, dirid):
return self.db.getnodes(dirid) return self.db.getnodes(dirid)
@memoize
def get_albums(self):
return self.db.getnodes(*[item["id"] for item in self.get_artists()])
@memoize @memoize
def get_filepath(self, nodeid): def get_filepath(self, nodeid):
parents = [self.db.getnode(nodeid)] parents = [self.db.getnode(nodeid)]

View File

@ -4,7 +4,9 @@ import logging
import mimetypes import mimetypes
from time import time from time import time
from threading import Thread from threading import Thread
from pysonic.types import KNOWN_MIMES from pysonic.types import KNOWN_MIMES, MUSIC_TYPES
from mutagen.id3 import ID3
from mutagen.id3._util import ID3NoHeaderError
logging = logging.getLogger("scanner") logging = logging.getLogger("scanner")
@ -93,5 +95,48 @@ class PysonicFilesystemScanner(object):
else: else:
logging.warning("Ignoring unreadable file at {}, unknown ftype ({}, {})" logging.warning("Ignoring unreadable file at {}, unknown ftype ({}, {})"
.format(fpath, ftype, extra)) .format(fpath, ftype, extra))
#
#
#
# Add advanced id3 metadata
for artist_dir in self.library.db.getnodes(parent["id"]):
artist = artist_dir["name"]
for album_dir in self.library.db.getnodes(artist_dir["id"]):
album = album_dir["name"]
album_meta = self.library.db.get_metadata(album_dir["id"])
for track_file in self.library.db.getnodes(album_dir["id"]):
track_meta = self.library.db.decode_metadata(track_file['metadata'])
title = track_file["name"]
fpath = self.library.get_filepath(track_file["id"])
if track_meta.get('id3_done', False) or track_file.get("type", "x") not in MUSIC_TYPES:
continue
print("Mutagening", fpath)
tags = {'id3_done': True}
try:
id3 = ID3(fpath)
# print(id3.pprint())
try:
tags["track"] = int(''.join(id3['TRCK'].text).split("/")[0])
except KeyError:
pass
try:
tags["id3_artist"] = ''.join(id3['TPE1'].text)
except KeyError:
pass
try:
tags["id3_album"] = ''.join(id3['TALB'].text)
except KeyError:
pass
try:
tags["id3_title"] = ''.join(id3['TIT2'].text)
except KeyError:
pass
try:
tags["id3_year"] = id3['TDRC'].text[0].year
except (KeyError, IndexError):
pass
except ID3NoHeaderError:
pass
self.library.db.update_metadata(track_file["id"], **tags)
logging.warning("Library scan complete in {}s".format(int(time() - start))) logging.warning("Library scan complete in {}s".format(int(time() - start)))