from __future__ import annotations import json from pathlib import Path from shared.utils.settings_bundle import is_wangp_settings_filename PLUGIN_DIR = Path(__file__).resolve().parent APP_ROOT_DIR = PLUGIN_DIR.parent.parent APP_SETTINGS_DIR = APP_ROOT_DIR / "settings" PROCESS_SETTINGS_DIR = PLUGIN_DIR / "settings" PROCESS_FULL_VIDEO_SETTINGS_FILE = APP_SETTINGS_DIR / "process_full_video_settings.json" LAUNCH_DEFAULT_PROCESS_NAME = "Outpaint Video - LTX 2.3 Distilled 1.1" USER_SETTINGS_STORAGE_KEY = "user_settings" USER_PROCESS_VALUE_PREFIX = "__user_settings__:" def load_process_definitions() -> tuple[dict[str, dict], str | None]: if not PROCESS_SETTINGS_DIR.is_dir(): return {}, f"Missing process settings folder: {PROCESS_SETTINGS_DIR}" process_definitions: dict[str, dict] = {} for settings_path in sorted(PROCESS_SETTINGS_DIR.glob("*.json")): try: raw_settings = json.loads(settings_path.read_text(encoding="utf-8")) except (OSError, json.JSONDecodeError) as exc: return {}, f"Unable to read process setting file {settings_path.name}: {exc}" if not isinstance(raw_settings, dict): return {}, f"Process setting file {settings_path.name} must contain a JSON object." process_name = str(settings_path.stem).strip() model_type = str(raw_settings.get("model_type") or "").strip() system_handler = str(raw_settings.get("system_handler") or "").strip() if len(process_name) == 0: return {}, f"Process setting file {settings_path.name} has an empty filename stem." if len(model_type) == 0 and len(system_handler) == 0: return {}, f"Process setting file {settings_path.name} is missing model_type." process_definitions[process_name] = {"settings": raw_settings, "path": str(settings_path)} if len(process_definitions) == 0: return {}, f"No process setting files were found in: {PROCESS_SETTINGS_DIR}" return process_definitions, None PROCESS_DEFINITIONS, PROCESS_DEFINITIONS_ERROR = load_process_definitions() DEFAULT_PROCESS_NAME = LAUNCH_DEFAULT_PROCESS_NAME if LAUNCH_DEFAULT_PROCESS_NAME in PROCESS_DEFINITIONS else next(iter(PROCESS_DEFINITIONS), "") DEFAULT_MODEL_TYPE = str(PROCESS_DEFINITIONS.get(DEFAULT_PROCESS_NAME, {}).get("settings", {}).get("model_type") or "") def load_saved_process_full_video_settings() -> dict: if not PROCESS_FULL_VIDEO_SETTINGS_FILE.is_file(): return {} try: raw_settings = json.loads(PROCESS_FULL_VIDEO_SETTINGS_FILE.read_text(encoding="utf-8")) except (OSError, json.JSONDecodeError) as exc: print(f"[Process Full Video] Warning: unable to read saved UI settings from {PROCESS_FULL_VIDEO_SETTINGS_FILE}: {exc}") return {} return raw_settings if isinstance(raw_settings, dict) else {} def save_process_full_video_settings(settings: dict) -> None: PROCESS_FULL_VIDEO_SETTINGS_FILE.parent.mkdir(parents=True, exist_ok=True) PROCESS_FULL_VIDEO_SETTINGS_FILE.write_text(json.dumps(settings, indent=2, ensure_ascii=True) + "\n", encoding="utf-8") def normalize_user_settings_ref(value) -> str: text = str(value or "").strip().strip('"').replace("\\", "/") if len(text) == 0 or text.startswith(("/", "./", "../")): return "" if len(text) >= 2 and text[1] == ":": return "" parts = [part.strip() for part in text.split("/") if len(part.strip()) > 0] if len(parts) != 2: return "" base_model_type, filename = parts filename = Path(filename).name if not is_wangp_settings_filename(filename): return "" return f"{base_model_type}/{filename}" def is_user_process_value(value) -> bool: return str(value or "").startswith(USER_PROCESS_VALUE_PREFIX) def user_process_value(ref: str) -> str: normalized = normalize_user_settings_ref(ref) return f"{USER_PROCESS_VALUE_PREFIX}{normalized}" if len(normalized) > 0 else "" def user_process_ref_from_value(value) -> str: text = str(value or "").strip() if not text.startswith(USER_PROCESS_VALUE_PREFIX): return "" return normalize_user_settings_ref(text[len(USER_PROCESS_VALUE_PREFIX):]) def get_saved_user_settings_refs(settings: dict | None) -> list[str]: raw_refs = settings.get(USER_SETTINGS_STORAGE_KEY, []) if isinstance(settings, dict) else [] if isinstance(raw_refs, str): raw_refs = [raw_refs] if not isinstance(raw_refs, list): return [] refs: list[str] = [] seen: set[str] = set() for raw_ref in raw_refs: ref = normalize_user_settings_ref(raw_ref) if len(ref) == 0 or ref.casefold() in seen: continue refs.append(ref) seen.add(ref.casefold()) return refs def store_user_settings_refs(refs: list[str]) -> None: saved_settings = load_saved_process_full_video_settings() normalized_refs = get_saved_user_settings_refs({USER_SETTINGS_STORAGE_KEY: refs}) saved_settings[USER_SETTINGS_STORAGE_KEY] = normalized_refs save_process_full_video_settings(saved_settings) def save_process_full_video_ui_settings(settings: dict) -> None: saved_settings = load_saved_process_full_video_settings() user_refs = get_saved_user_settings_refs(saved_settings) next_settings = dict(settings) next_settings[USER_SETTINGS_STORAGE_KEY] = user_refs save_process_full_video_settings(next_settings) def save_process_full_video_selection(process_model_type: str, process_name: str) -> None: saved_settings = load_saved_process_full_video_settings() saved_settings["process_model_type"] = str(process_model_type or "").strip() saved_settings["process_name"] = str(process_name or "").strip() save_process_full_video_settings(saved_settings)