# /// script # requires-python = ">=3.12" # # [tool.orcaslicer.plugin] # id = "reload-local-bundle-demo" # name = "Reload Local Bundle Demo" # description = "Manages local filament, process, and printer profiles with live bundle reload." # author = "OrcaSlicer contributors" # version = "0.1.0" # /// """A small interactive consumer of ``orca.host.preset.bundles.reload_local``.""" import json import os import tempfile import orca BUNDLE_ID = "reload-local-bundle-demo" STATE_FILE = ".reload-local-bundle-demo-state.json" DEMO_PRESETS = ( { "id": "filament", "name": "Local Bundle Demo Filament", "kind": "Filament", "type": "filament", "directory": "filament", "collection": "filaments", "setting_key": "filament_settings_id", }, { "id": "process", "name": "Local Bundle Demo Process", "kind": "Process", "type": "process", "directory": "process", "collection": "prints", "setting_key": "print_settings_id", }, { "id": "printer", "name": "Local Bundle Demo Printer", "kind": "Printer", "type": "machine", "directory": "machine", "collection": "printers", "setting_key": "printer_settings_id", }, ) def _data_dir(): parts = os.path.abspath(__file__).replace("\\", "/").split("/") if "orca_plugins" not in parts: raise RuntimeError("Install this file as a local OrcaSlicer plugin") return "/".join(parts[: parts.index("orca_plugins")]) def _folder_from_preset_file(path): parts = os.path.normpath(path or "").replace("\\", "/").split("/") try: return parts[parts.index("user") + 1] except (ValueError, IndexError): return None def _bundle_dir(bundle): for collection in (bundle.filaments, bundle.prints, bundle.printers): for index in range(collection.size()): folder = _folder_from_preset_file(collection.preset(index).file) if folder: return os.path.join(_data_dir(), "user", folder, "_local", BUNDLE_ID) return os.path.join(_data_dir(), "user", "default", "_local", BUNDLE_ID) def _source_path(bundle, item): return os.path.join(_bundle_dir(bundle), item["directory"], item["name"] + ".json") def _state_path(bundle): return os.path.join(_bundle_dir(bundle), STATE_FILE) def _write_json_atomic(path, value): os.makedirs(os.path.dirname(path), exist_ok=True) descriptor, temporary = tempfile.mkstemp( prefix="." + os.path.basename(path) + ".", suffix=".tmp", dir=os.path.dirname(path) ) try: with os.fdopen(descriptor, "w", encoding="utf-8") as stream: json.dump(value, stream, ensure_ascii=False, indent=2) stream.write("\n") os.replace(temporary, path) except Exception: try: os.unlink(temporary) except OSError: pass raise def _remove_file(path): try: os.unlink(path) except FileNotFoundError: pass def _read_state(bundle): try: with open(_state_path(bundle), "r", encoding="utf-8") as stream: state = json.load(stream) except FileNotFoundError: return {"profiles": {}} except (OSError, ValueError) as error: raise RuntimeError("The demo state file cannot be read: %s" % error) if not isinstance(state, dict) or not isinstance(state.get("profiles"), dict): raise RuntimeError("The demo state file has an invalid shape") return state def _profile_state(state, item): profiles = state["profiles"] profile = profiles.get(item["id"]) if profile is None: profile = {} profiles[item["id"]] = profile if not isinstance(profile, dict): raise RuntimeError("The demo state for %s has an invalid shape" % item["name"]) profile["enabled"] = profile.get("enabled") is True profile["deleted"] = profile.get("deleted") is True if profile["deleted"]: profile["enabled"] = False profile["parent_name"] = str(profile.get("parent_name") or "") return profile def _collection(bundle, item): return getattr(bundle, item["collection"]) def _target_name(item): return "_local/%s/%s" % (BUNDLE_ID, item["name"]) def _parent_name(bundle, item): collection = _collection(bundle, item) own_names = { _target_name(candidate) for candidate in DEMO_PRESETS if candidate["collection"] == item["collection"] } candidates = [collection.selected_preset()] candidates.extend(collection.preset(index) for index in range(collection.size())) for preset in candidates: if preset.name not in own_names: return preset.name raise RuntimeError("A parent %s preset is required for this demo" % item["kind"].lower()) def _parent_is_live(bundle, item, name): return bool(name) and name not in {_target_name(item)} and _collection(bundle, item).find_preset(name) is not None def _profile(item, parent_name): return { "type": item["type"], "name": item["name"], "from": "User", "instantiation": "true", "version": "1.0.0", "inherits": parent_name, item["setting_key"]: item["name"], } def _reconcile(bundle, state): for item in DEMO_PRESETS: profile = _profile_state(state, item) if profile["enabled"] and not profile["deleted"] and not _parent_is_live(bundle, item, profile["parent_name"]): profile["parent_name"] = _parent_name(bundle, item) for item in DEMO_PRESETS: profile = _profile_state(state, item) path = _source_path(bundle, item) if profile["enabled"] and not profile["deleted"]: _write_json_atomic(path, _profile(item, profile["parent_name"])) else: _remove_file(path) _write_json_atomic( os.path.join(_bundle_dir(bundle), "bundle_metadata.json"), {"id": BUNDLE_ID, "name": "Reload Local Bundle Demo", "version": "0.1.0"}, ) _write_json_atomic(_state_path(bundle), state) orca.host.preset.bundles.reload_local(BUNDLE_ID) def _state_payload(bundle, state, message="", error=""): items = [] for item in DEMO_PRESETS: profile = _profile_state(state, item) items.append( { "id": item["id"], "name": item["name"], "kind": item["kind"], "enabled": profile["enabled"], "deleted": profile["deleted"], "loaded": _collection(bundle, item).find_preset(_target_name(item)) is not None, } ) return { "command": "state", "items": items, "bundles": orca.host.preset.bundles.list_ids(), "message": message, "error": error, } PAGE = r"""
Each row controls one profile in this local bundle. A switch adds or removes its source file; Delete removes the demo record and Restore returns it disabled. Every action reloads this bundle across filament, process, and machine. Local bundles lists every bundle id that orca.host.preset.bundles.list_ids() reports; Reload calls orca.host.preset.bundles.reload_local() for that id.