file_manager: save metadata to the database
This allows gcode file metadata to persist through restarts, reducing the time spent parsing. The gcode file path also persists, so it is available even when Klippy is not. Signed-off-by: Eric Callahan <arksine.code@gmail.com>
This commit is contained in:
parent
7ff54d8f2f
commit
87e136003f
|
@ -22,7 +22,9 @@ class FileManager:
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
self.server = config.get_server()
|
self.server = config.get_server()
|
||||||
self.file_paths = {}
|
self.file_paths = {}
|
||||||
self.gcode_metadata = MetadataStorage(self.server)
|
database = self.server.load_plugin(config, "database")
|
||||||
|
gc_path = database.get_item("moonraker", "file_manager.gcode_path", "")
|
||||||
|
self.gcode_metadata = MetadataStorage(self.server, gc_path, database)
|
||||||
self.fixed_path_args = {}
|
self.fixed_path_args = {}
|
||||||
|
|
||||||
# Register file management endpoints
|
# Register file management endpoints
|
||||||
|
@ -58,6 +60,10 @@ class FileManager:
|
||||||
raise config.error(
|
raise config.error(
|
||||||
"Option 'config_path' is not a valid directory")
|
"Option 'config_path' is not a valid directory")
|
||||||
|
|
||||||
|
# If gcode path is in the database, register it
|
||||||
|
if gc_path:
|
||||||
|
self.register_directory('gcodes', gc_path)
|
||||||
|
|
||||||
def _update_fixed_paths(self):
|
def _update_fixed_paths(self):
|
||||||
kinfo = self.server.get_klippy_info()
|
kinfo = self.server.get_klippy_info()
|
||||||
paths = {k: kinfo.get(k) for k in
|
paths = {k: kinfo.get(k) for k in
|
||||||
|
@ -108,7 +114,10 @@ class FileManager:
|
||||||
self.file_paths[root] = path
|
self.file_paths[root] = path
|
||||||
self.server.register_static_file_handler(root, path)
|
self.server.register_static_file_handler(root, path)
|
||||||
if root == "gcodes":
|
if root == "gcodes":
|
||||||
# scan metadata
|
database = self.server.lookup_plugin(
|
||||||
|
"database").wrap_namespace("moonraker")
|
||||||
|
database["file_manager.gcode_path"] = path
|
||||||
|
# scan for metadata changes
|
||||||
self.gcode_metadata.update_gcode_path(path)
|
self.gcode_metadata.update_gcode_path(path)
|
||||||
try:
|
try:
|
||||||
self.get_file_list("gcodes")
|
self.get_file_list("gcodes")
|
||||||
|
@ -612,22 +621,25 @@ class FileManager:
|
||||||
|
|
||||||
|
|
||||||
METADATA_PRUNE_TIME = 600000
|
METADATA_PRUNE_TIME = 600000
|
||||||
|
METADATA_NAMESPACE = "gcode_metadata"
|
||||||
|
|
||||||
class MetadataStorage:
|
class MetadataStorage:
|
||||||
def __init__(self, server):
|
def __init__(self, server, gc_path, database):
|
||||||
self.server = server
|
self.server = server
|
||||||
self.metadata = {}
|
database.register_local_namespace(METADATA_NAMESPACE)
|
||||||
|
self.mddb = database.wrap_namespace(
|
||||||
|
METADATA_NAMESPACE, parse_keys=False)
|
||||||
self.pending_requests = {}
|
self.pending_requests = {}
|
||||||
self.events = {}
|
self.events = {}
|
||||||
self.busy = False
|
self.busy = False
|
||||||
self.gc_path = os.path.expanduser("~")
|
self.gc_path = gc_path
|
||||||
self.prune_cb = PeriodicCallback(
|
self.prune_cb = PeriodicCallback(
|
||||||
self.prune_metadata, METADATA_PRUNE_TIME)
|
self.prune_metadata, METADATA_PRUNE_TIME)
|
||||||
|
|
||||||
def update_gcode_path(self, path):
|
def update_gcode_path(self, path):
|
||||||
if path == self.gc_path:
|
if path == self.gc_path:
|
||||||
return
|
return
|
||||||
self.metadata = {}
|
self.mddb.clear()
|
||||||
self.gc_path = path
|
self.gc_path = path
|
||||||
if not self.prune_cb.is_running():
|
if not self.prune_cb.is_running():
|
||||||
self.prune_cb.start()
|
self.prune_cb.start()
|
||||||
|
@ -636,27 +648,28 @@ class MetadataStorage:
|
||||||
self.prune_cb.stop()
|
self.prune_cb.stop()
|
||||||
|
|
||||||
def get(self, key, default=None):
|
def get(self, key, default=None):
|
||||||
if key not in self.metadata:
|
return self.mddb.get(key, default)
|
||||||
return default
|
|
||||||
return dict(self.metadata[key])
|
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
return dict(self.metadata[key])
|
return self.mddb[key]
|
||||||
|
|
||||||
def prune_metadata(self):
|
def prune_metadata(self):
|
||||||
for fname in list(self.metadata.keys()):
|
for fname in list(self.mddb.keys()):
|
||||||
fpath = os.path.join(self.gc_path, fname)
|
fpath = os.path.join(self.gc_path, fname)
|
||||||
if not os.path.exists(fpath):
|
if not os.path.exists(fpath):
|
||||||
del self.metadata[fname]
|
del self.mddb[fname]
|
||||||
logging.info(f"Pruned file: {fname}")
|
logging.info(f"Pruned file: {fname}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
def _has_valid_data(self, fname, fsize, modified):
|
def _has_valid_data(self, fname, fsize, modified):
|
||||||
mdata = self.metadata.get(fname, {'size': "", 'modified': 0})
|
mdata = self.mddb.get(fname, {'size': "", 'modified': 0})
|
||||||
return mdata['size'] == fsize and mdata['modified'] == modified
|
return mdata['size'] == fsize and mdata['modified'] == modified
|
||||||
|
|
||||||
def remove_file(self, fname):
|
def remove_file(self, fname):
|
||||||
self.metadata.pop(fname)
|
try:
|
||||||
|
del self.mddb[fname]
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
def parse_metadata(self, fname, fsize, modified, notify=False):
|
def parse_metadata(self, fname, fsize, modified, notify=False):
|
||||||
evt = Event()
|
evt = Event()
|
||||||
|
@ -689,7 +702,7 @@ class MetadataStorage:
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
self.metadata[fname] = {'size': fsize, 'modified': modified}
|
self.mddb[fname] = {'size': fsize, 'modified': modified}
|
||||||
logging.info(
|
logging.info(
|
||||||
f"Unable to extract medatadata from file: {fname}")
|
f"Unable to extract medatadata from file: {fname}")
|
||||||
evt.set()
|
evt.set()
|
||||||
|
@ -716,7 +729,7 @@ class MetadataStorage:
|
||||||
if not metadata:
|
if not metadata:
|
||||||
# This indicates an error, do not add metadata for this
|
# This indicates an error, do not add metadata for this
|
||||||
raise self.server.error("Unable to extract metadata")
|
raise self.server.error("Unable to extract metadata")
|
||||||
self.metadata[path] = dict(metadata)
|
self.mddb[path] = dict(metadata)
|
||||||
metadata['filename'] = path
|
metadata['filename'] = path
|
||||||
if notify:
|
if notify:
|
||||||
self.server.send_event(
|
self.server.send_event(
|
||||||
|
|
Loading…
Reference in New Issue