From 87e136003f5ec0db9d4c8ea06358d3da806b2d49 Mon Sep 17 00:00:00 2001 From: Arksine Date: Tue, 23 Feb 2021 20:26:56 -0500 Subject: [PATCH] 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 --- moonraker/plugins/file_manager.py | 45 ++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/moonraker/plugins/file_manager.py b/moonraker/plugins/file_manager.py index 441b2c8..c30c790 100644 --- a/moonraker/plugins/file_manager.py +++ b/moonraker/plugins/file_manager.py @@ -22,7 +22,9 @@ class FileManager: def __init__(self, config): self.server = config.get_server() 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 = {} # Register file management endpoints @@ -58,6 +60,10 @@ class FileManager: raise config.error( "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): kinfo = self.server.get_klippy_info() paths = {k: kinfo.get(k) for k in @@ -108,7 +114,10 @@ class FileManager: self.file_paths[root] = path self.server.register_static_file_handler(root, path) 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) try: self.get_file_list("gcodes") @@ -612,22 +621,25 @@ class FileManager: METADATA_PRUNE_TIME = 600000 +METADATA_NAMESPACE = "gcode_metadata" class MetadataStorage: - def __init__(self, server): + def __init__(self, server, gc_path, database): 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.events = {} self.busy = False - self.gc_path = os.path.expanduser("~") + self.gc_path = gc_path self.prune_cb = PeriodicCallback( self.prune_metadata, METADATA_PRUNE_TIME) def update_gcode_path(self, path): if path == self.gc_path: return - self.metadata = {} + self.mddb.clear() self.gc_path = path if not self.prune_cb.is_running(): self.prune_cb.start() @@ -636,27 +648,28 @@ class MetadataStorage: self.prune_cb.stop() def get(self, key, default=None): - if key not in self.metadata: - return default - return dict(self.metadata[key]) + return self.mddb.get(key, default) def __getitem__(self, key): - return dict(self.metadata[key]) + return self.mddb[key] 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) if not os.path.exists(fpath): - del self.metadata[fname] + del self.mddb[fname] logging.info(f"Pruned file: {fname}") continue 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 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): evt = Event() @@ -689,7 +702,7 @@ class MetadataStorage: else: break else: - self.metadata[fname] = {'size': fsize, 'modified': modified} + self.mddb[fname] = {'size': fsize, 'modified': modified} logging.info( f"Unable to extract medatadata from file: {fname}") evt.set() @@ -716,7 +729,7 @@ class MetadataStorage: if not metadata: # This indicates an error, do not add metadata for this raise self.server.error("Unable to extract metadata") - self.metadata[path] = dict(metadata) + self.mddb[path] = dict(metadata) metadata['filename'] = path if notify: self.server.send_event(