From b79e3b309be124afba04066d36995b1bfcc830e2 Mon Sep 17 00:00:00 2001 From: Nose Date: Fri, 23 Jan 2026 18:40:00 -0800 Subject: [PATCH] h --- API/data/alldebrid.json | 6 ++--- MPV/lyric.py | 19 +------------- SYS/config.py | 55 +++++++++++++++++++++++++++++++++++------ cmdnat/config.py | 14 +++++------ 4 files changed, 57 insertions(+), 37 deletions(-) diff --git a/API/data/alldebrid.json b/API/data/alldebrid.json index ecef41e..8bde55d 100644 --- a/API/data/alldebrid.json +++ b/API/data/alldebrid.json @@ -339,7 +339,7 @@ "(file\\.al/[0-9a-zA-Z]{12})" ], "regexp": "(file\\.al/[0-9a-zA-Z]{12})", - "status": true + "status": false }, "filedot": { "name": "filedot", @@ -477,7 +477,7 @@ "isra\\.cloud/\\?op=report_file&id=([0-9a-zA-Z]{12})" ], "regexp": "((isra\\.cloud/[0-9a-zA-Z]{12}))|(isra\\.cloud/\\?op=report_file&id=([0-9a-zA-Z]{12}))", - "status": true, + "status": false, "hardRedirect": [ "isra\\.cloud/([0-9a-zA-Z]{12})" ] @@ -507,7 +507,7 @@ "mediafire\\.com/(\\?|download/|file/|download\\.php\\?)([0-9a-z]{15})" ], "regexp": "mediafire\\.com/(\\?|download/|file/|download\\.php\\?)([0-9a-z]{15})", - "status": false + "status": true }, "mexashare": { "name": "mexashare", diff --git a/MPV/lyric.py b/MPV/lyric.py index d6bdf5d..e337d97 100644 --- a/MPV/lyric.py +++ b/MPV/lyric.py @@ -557,24 +557,7 @@ def _load_config_best_effort() -> dict: try: from SYS.config import load_config - # `SYS.config.load_config()` defaults to loading `config.conf` from the - # SYS/ directory, but this repo keeps `config.conf` at the repo root. - # MPV.lyric is often spawned from mpv (not the CLI), so we must locate - # the repo root ourselves. - try: - repo_root = Path(__file__).resolve().parent.parent - except Exception: - repo_root = None - - cfg = None - if repo_root is not None: - try: - cfg = load_config(config_dir=repo_root) - except Exception: - cfg = None - - if cfg is None: - cfg = load_config() + cfg = load_config() return cfg if isinstance(cfg, dict) else {} except Exception: diff --git a/SYS/config.py b/SYS/config.py index 670713a..0833f95 100644 --- a/SYS/config.py +++ b/SYS/config.py @@ -2,13 +2,12 @@ from __future__ import annotations -import re -import tempfile import json import sqlite3 import time +from copy import deepcopy from pathlib import Path -from typing import Any, Dict, Optional, List +from typing import Any, Dict, List, Optional, Tuple from SYS.logger import log from SYS.utils import expand_path from SYS.database import db, get_config_all, save_config_value @@ -16,6 +15,7 @@ from SYS.database import db, get_config_all, save_config_value SCRIPT_DIR = Path(__file__).resolve().parent _CONFIG_CACHE: Dict[str, Any] = {} +_LAST_SAVED_CONFIG: Dict[str, Any] = {} _CONFIG_SAVE_MAX_RETRIES = 5 _CONFIG_SAVE_RETRY_DELAY = 0.15 @@ -39,9 +39,10 @@ def global_config() -> List[Dict[str, Any]]: def clear_config_cache() -> None: - """Clear the configuration cache.""" - global _CONFIG_CACHE + """Clear the configuration cache and baseline snapshot.""" + global _CONFIG_CACHE, _LAST_SAVED_CONFIG _CONFIG_CACHE = {} + _LAST_SAVED_CONFIG = {} def get_hydrus_instance( @@ -334,8 +335,37 @@ def resolve_debug_log(config: Dict[str, Any]) -> Optional[Path]: return path +def _flatten_config_entries(config: Dict[str, Any]) -> Dict[Tuple[str, str, str, str], Any]: + entries: Dict[Tuple[str, str, str, str], Any] = {} + for key, value in config.items(): + if key in ('store', 'provider', 'tool') and isinstance(value, dict): + for subtype, instances in value.items(): + if not isinstance(instances, dict): + continue + if key == 'store': + for name, settings in instances.items(): + if not isinstance(settings, dict): + continue + for k, v in settings.items(): + entries[(key, subtype, name, k)] = v + else: + for k, v in instances.items(): + entries[(key, subtype, 'default', k)] = v + elif not key.startswith('_') and value is not None: + entries[('global', 'none', 'none', key)] = value + return entries + + +def _count_changed_entries(old_config: Dict[str, Any], new_config: Dict[str, Any]) -> int: + old_entries = _flatten_config_entries(old_config or {}) + new_entries = _flatten_config_entries(new_config or {}) + changed = {k for k, v in new_entries.items() if old_entries.get(k) != v} + removed = {k for k in old_entries if k not in new_entries} + return len(changed) + len(removed) + + def load_config() -> Dict[str, Any]: - global _CONFIG_CACHE + global _CONFIG_CACHE, _LAST_SAVED_CONFIG if _CONFIG_CACHE: return _CONFIG_CACHE @@ -343,8 +373,10 @@ def load_config() -> Dict[str, Any]: db_config = get_config_all() if db_config: _CONFIG_CACHE = db_config + _LAST_SAVED_CONFIG = deepcopy(db_config) return db_config + _LAST_SAVED_CONFIG = {} return {} @@ -354,6 +386,10 @@ def reload_config() -> Dict[str, Any]: def save_config(config: Dict[str, Any]) -> int: + global _CONFIG_CACHE, _LAST_SAVED_CONFIG + previous_config = deepcopy(_LAST_SAVED_CONFIG) + changed_count = _count_changed_entries(previous_config, config) + def _write_entries() -> int: count = 0 with db.transaction(): @@ -384,7 +420,10 @@ def save_config(config: Dict[str, Any]) -> int: while True: try: saved_entries = _write_entries() - log(f"Synced {saved_entries} entries to {db.db_path}") + log( + f"Synced {saved_entries} entries to {db.db_path} " + f"({changed_count} changed entries)" + ) break except sqlite3.OperationalError as exc: attempts += 1 @@ -400,8 +439,8 @@ def save_config(config: Dict[str, Any]) -> int: raise clear_config_cache() - global _CONFIG_CACHE _CONFIG_CACHE = config + _LAST_SAVED_CONFIG = deepcopy(config) return saved_entries diff --git a/cmdnat/config.py b/cmdnat/config.py index f23bc66..c624792 100644 --- a/cmdnat/config.py +++ b/cmdnat/config.py @@ -182,11 +182,9 @@ def _strip_value_quotes(value: str) -> str: def _run(piped_result: Any, args: List[str], config: Dict[str, Any]) -> int: import sys - from pathlib import Path - - # Load from workspace root, not SYS directory - workspace_root = Path(__file__).resolve().parent.parent - current_config = load_config(config_dir=workspace_root) + + # Load configuration from the database + current_config = load_config() selection_key = _get_selected_config_key() value_from_args = _extract_value_arg(args) if selection_key else None @@ -202,7 +200,7 @@ def _run(piped_result: Any, args: List[str], config: Dict[str, Any]) -> int: new_value = _strip_value_quotes(new_value) try: set_nested_config(current_config, selection_key, new_value) - save_config(current_config, config_dir=workspace_root) + save_config(current_config) print(f"Updated '{selection_key}' to '{new_value}'") return 0 except Exception as exc: @@ -235,7 +233,7 @@ def _run(piped_result: Any, args: List[str], config: Dict[str, Any]) -> int: from cmdlet._shared import SharedArgs from cmdnat.status import CMDLET as STATUS_CMDLET # We reload the config one more time because it might have changed on disk - fresh_config = load_config(config_dir=workspace_root) + fresh_config = load_config() # Force refresh of shared caches (especially stores) SharedArgs._refresh_store_choices_cache(fresh_config) @@ -261,7 +259,7 @@ def _run(piped_result: Any, args: List[str], config: Dict[str, Any]) -> int: value = _strip_value_quotes(" ".join(args[1:])) try: set_nested_config(current_config, key, value) - save_config(current_config, config_dir=workspace_root) + save_config(current_config) print(f"Updated '{key}' to '{value}'") return 0 except Exception as exc: