add id3 scanner and support more apis
This commit is contained in:
parent
99aae4165f
commit
2b87e1d2ec
|
@ -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()
|
||||||
|
|
||||||
|
|
|
@ -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({
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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)))
|
||||||
|
|
Loading…
Reference in New Issue