2017-08-13 18:56:13 -07:00
|
|
|
import os
|
|
|
|
import json
|
2017-08-13 21:13:46 -07:00
|
|
|
import logging
|
|
|
|
import mimetypes
|
|
|
|
from time import time
|
2017-08-13 18:56:13 -07:00
|
|
|
from threading import Thread
|
2017-08-13 22:08:40 -07:00
|
|
|
from pysonic.types import KNOWN_MIMES
|
2017-08-13 18:56:13 -07:00
|
|
|
|
|
|
|
|
2017-08-13 21:13:46 -07:00
|
|
|
logging = logging.getLogger("scanner")
|
|
|
|
|
|
|
|
|
2017-08-13 18:56:13 -07:00
|
|
|
class PysonicFilesystemScanner(object):
|
|
|
|
def __init__(self, library):
|
|
|
|
self.library = library
|
|
|
|
|
|
|
|
def init_scan(self):
|
|
|
|
self.scanner = Thread(target=self.rescan, daemon=True)
|
|
|
|
self.scanner.start()
|
|
|
|
|
|
|
|
def rescan(self):
|
|
|
|
# Perform directory scan
|
2017-08-13 21:13:46 -07:00
|
|
|
logging.warning("Beginning library rescan")
|
|
|
|
start = time()
|
2017-08-13 18:56:13 -07:00
|
|
|
for parent in self.library.get_libraries():
|
|
|
|
meta = json.loads(parent["metadata"])
|
2017-08-13 21:13:46 -07:00
|
|
|
logging.info("Scanning {}".format(meta["fspath"]))
|
2017-08-13 18:56:13 -07:00
|
|
|
|
|
|
|
def recurse_dir(path, parent):
|
2017-08-13 21:13:46 -07:00
|
|
|
logging.info("Scanning {} with parent {}".format(path, parent))
|
2017-08-13 18:56:13 -07:00
|
|
|
# create or update the database of nodes by comparing sets of names
|
|
|
|
fs_entries = set(os.listdir(path))
|
|
|
|
db_entires = self.library.db.getnodes(parent["id"])
|
|
|
|
db_entires_names = set([i['name'] for i in db_entires])
|
|
|
|
to_delete = db_entires_names - fs_entries
|
|
|
|
to_create = fs_entries - db_entires_names
|
|
|
|
|
|
|
|
# Create any nodes not found in the db
|
|
|
|
for create in to_create:
|
2017-08-13 21:13:46 -07:00
|
|
|
new_node = self.library.db.addnode(parent["id"], path, create)
|
2017-08-13 22:08:40 -07:00
|
|
|
logging.info("Added {}".format(os.path.join(path, create)))
|
2017-08-13 18:56:13 -07:00
|
|
|
db_entires.append(new_node)
|
|
|
|
|
|
|
|
# Delete any db nodes not found on disk
|
|
|
|
for delete in to_delete:
|
2017-08-13 21:13:46 -07:00
|
|
|
logging.info("Prune ", delete, "in parent", path)
|
2017-08-13 18:56:13 -07:00
|
|
|
node = [i for i in db_entires if i["name"] == delete]
|
|
|
|
if node:
|
|
|
|
deleted = self.library.db.delnode(node[0]["id"])
|
2017-08-13 21:13:46 -07:00
|
|
|
logging.info("Pruned {}, deleting total of {}".format(node, deleted))
|
2017-08-13 18:56:13 -07:00
|
|
|
|
|
|
|
for entry in db_entires:
|
|
|
|
if entry["name"] in to_delete:
|
|
|
|
continue
|
|
|
|
|
|
|
|
if int(entry['isdir']): # 1 means dir
|
|
|
|
recurse_dir(os.path.join(path, entry["name"]), entry)
|
|
|
|
# Populate all files for this top-level root
|
|
|
|
recurse_dir(meta["fspath"], parent)
|
|
|
|
#
|
|
|
|
#
|
|
|
|
#
|
|
|
|
# Add simple 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"]):
|
|
|
|
title = track_file["name"]
|
|
|
|
if not track_file["title"]:
|
|
|
|
self.library.db.update_metadata(track_file["id"], artist=artist, album=album, title=title)
|
2017-08-13 21:13:46 -07:00
|
|
|
logging.info("Adding simple metadata for {}/{}/{} #{}".format(artist, album,
|
|
|
|
title, track_file["id"]))
|
2017-08-13 18:56:13 -07:00
|
|
|
if not album_dir["album"]:
|
|
|
|
self.library.db.update_metadata(album_dir["id"], artist=artist, album=album)
|
2017-08-13 21:13:46 -07:00
|
|
|
logging.info("Adding simple metadata for {}/{} #{}".format(artist, album, album_dir["id"]))
|
2017-08-13 18:56:13 -07:00
|
|
|
if not artist_dir["artist"]:
|
|
|
|
self.library.db.update_metadata(artist_dir["id"], artist=artist)
|
2017-08-13 21:13:46 -07:00
|
|
|
logging.info("Adding simple metadata for {} #{}".format(artist, artist_dir["id"]))
|
|
|
|
if title in ["cover.jpg", "cover.png"] and 'cover' not in album_meta:
|
2017-08-13 18:56:13 -07:00
|
|
|
# // add cover art
|
|
|
|
self.library.db.update_metadata(album_dir["id"], cover=track_file["id"])
|
2017-08-13 21:13:46 -07:00
|
|
|
logging.info("added cover for {}".format(album_dir['id']))
|
|
|
|
|
|
|
|
if track_file["type"] is None:
|
|
|
|
fpath = self.library.get_filepath(track_file['id'])
|
|
|
|
ftype, extra = mimetypes.guess_type(fpath)
|
|
|
|
|
|
|
|
if ftype in KNOWN_MIMES:
|
|
|
|
self.library.db.update_metadata(track_file["id"], type=ftype)
|
|
|
|
logging.info("added type {} for {}".format(ftype, track_file['id']))
|
|
|
|
else:
|
|
|
|
logging.warning("Ignoring unreadable file at {}, unknown ftype ({}, {})"
|
|
|
|
.format(fpath, ftype, extra))
|
|
|
|
|
|
|
|
logging.warning("Library scan complete in {}s".format(int(time() - start)))
|