diff --git a/SYS/config.py b/SYS/config.py index bb7b89d..efa948b 100644 --- a/SYS/config.py +++ b/SYS/config.py @@ -800,6 +800,15 @@ def save_config_and_verify(config: Dict[str, Any], retries: int = 3, delay: floa store_key = None if prov_key == expected_key or store_key == expected_key: + try: + # Log a short, masked fingerprint to aid debugging without exposing the key itself + import hashlib + + fp = hashlib.sha256(expected_key.encode("utf-8")).hexdigest()[:8] + log(f"Verified AllDebrid API key persisted (fingerprint={fp})") + except Exception: + # If hashing/logging fails, don't abort the save + pass return saved # Not yet persisted; log and retry diff --git a/TUI/modalscreen/config_modal.py b/TUI/modalscreen/config_modal.py index 5355895..141771e 100644 --- a/TUI/modalscreen/config_modal.py +++ b/TUI/modalscreen/config_modal.py @@ -10,7 +10,7 @@ from textual.screen import ModalScreen from textual.widgets import Static, Button, Input, Label, ListView, ListItem, Rule, Select, Checkbox from pathlib import Path -from SYS.config import load_config, save_config, reload_config, global_config, count_changed_entries, ConfigSaveConflict +from SYS.config import load_config, save_config, save_config_and_verify, reload_config, global_config, count_changed_entries, ConfigSaveConflict from SYS.database import db from SYS.logger import log, debug from Store.registry import _discover_store_classes, _required_keys_for @@ -1739,7 +1739,10 @@ class ConfigModal(ModalScreen): @work(thread=True) def _save_background(self, cfg: Dict[str, Any], changed: int) -> None: try: - saved_entries = save_config(cfg) + # Use the verified save path which will check that crucial keys + # (like AllDebrid API keys) persisted to disk. This ensures the UI + # surface reports a failure immediately if post-save verification fails. + saved_entries = save_config_and_verify(cfg) try: appobj = self.app except Exception: @@ -1758,6 +1761,17 @@ class ConfigModal(ModalScreen): appobj.call_from_thread(self._on_save_complete, False, str(exc), changed, 0) else: self._on_save_complete(False, str(exc), changed, 0) + except Exception as exc: + # Bubble up verification/other save errors back to the UI so the + # user knows persistent storage failed. + try: + appobj = self.app + except Exception: + appobj = None + if appobj and hasattr(appobj, 'call_from_thread'): + appobj.call_from_thread(self._on_save_complete, False, str(exc), changed, 0) + else: + self._on_save_complete(False, str(exc), changed, 0) except Exception as exc: try: appobj = self.app diff --git a/cmdnat/config.py b/cmdnat/config.py index c624792..f0e8f35 100644 --- a/cmdnat/config.py +++ b/cmdnat/config.py @@ -1,7 +1,7 @@ from typing import List, Dict, Any, Optional, Sequence from cmdlet._shared import Cmdlet, CmdletArg -from SYS.config import load_config, save_config +from SYS.config import load_config, save_config, save_config_and_verify from SYS import pipeline as ctx from SYS.result_table import Table @@ -200,7 +200,20 @@ 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) + # For AllDebrid API changes, use the verified save path to ensure + # the new API key persisted to disk; otherwise fall back to normal save. + try: + key_l = str(selection_key or "").lower() + except Exception: + key_l = "" + if "alldebrid" in key_l or "all-debrid" in key_l: + try: + save_config_and_verify(current_config) + except Exception as exc: + print(f"Error saving configuration (verification failed): {exc}") + return 1 + else: + save_config(current_config) print(f"Updated '{selection_key}' to '{new_value}'") return 0 except Exception as exc: diff --git a/logs/log_fallback.txt b/logs/log_fallback.txt index 7aa863d..295158d 100644 --- a/logs/log_fallback.txt +++ b/logs/log_fallback.txt @@ -572,3 +572,38 @@ http://10.162.158.28:45899/get_files/file?hash=5c7296f1a5544522e3d118f60080e0389 2026-02-01T04:23:19.808488Z [DEBUG] logger.debug: DEBUG: [Store] Unknown store type 'debrid' 2026-02-01T04:23:36.282589Z [DEBUG] search_file.run: Backend all-debrid search failed: "Unknown store backend: all-debrid. Available: ['rpi', 'local']" 2026-02-01T04:23:52.712154Z [DEBUG] logger.debug: DEBUG: [search-file] Searching 'local' +2026-02-01T04:40:52.223912Z [DEBUG] logger.debug: DEBUG: [search-file] Calling alldebrid.search(filters={}) +2026-02-01T04:41:09.154528Z [DEBUG] logger.debug: DEBUG: +2026-02-01T04:41:26.087564Z [DEBUG] logger.debug: DEBUG: +2026-02-01T04:41:43.108223Z [DEBUG] logger.debug: DEBUG: [search-file] alldebrid -> 50 result(s) +2026-02-01T04:42:00.084311Z [DEBUG] logger.debug: DEBUG: [search-file] Calling alldebrid.search(filters={}) +2026-02-01T04:42:17.153684Z [DEBUG] logger.debug: DEBUG: +2026-02-01T04:42:33.940496Z [DEBUG] logger.debug: DEBUG: +2026-02-01T04:42:50.766611Z [DEBUG] logger.debug: DEBUG: [search-file] alldebrid -> 50 result(s) +2026-02-01T04:43:07.583583Z [DEBUG] logger.debug: DEBUG: [search-file] Calling alldebrid.search(filters={}) +2026-02-01T04:43:24.248810Z [DEBUG] logger.debug: DEBUG: +2026-02-01T04:43:40.777131Z [DEBUG] logger.debug: DEBUG: +2026-02-01T04:43:57.669799Z [DEBUG] logger.debug: DEBUG: [search-file] alldebrid -> 50 result(s) +2026-02-01T04:45:05.508224Z [DEBUG] logger.debug: DEBUG: [download-file] run invoked with args: ['magnet:?xt=urn:btih:RO3NJTH7KWG3YIZM6ACTLJOFMF4IXK64'] +2026-02-01T04:45:22.402282Z [DEBUG] logger.debug: DEBUG: Starting download-file +2026-02-01T04:45:39.392199Z [DEBUG] alldebrid.url_patterns: [alldebrid] url_patterns loaded 0 cached host domains; total patterns=2 +2026-02-01T04:45:56.042183Z [DEBUG] alldebrid.url_patterns: [alldebrid] url_patterns loaded 0 cached host domains; total patterns=2 +2026-02-01T04:46:12.642265Z [DEBUG] alldebrid.url_patterns: [alldebrid] url_patterns loaded 0 cached host domains; total patterns=2 +2026-02-01T04:46:29.352428Z [DEBUG] logger.debug: DEBUG: Output directory: C:\Users\Admin\AppData\Local\Temp\Medios-Macina +2026-02-01T04:46:46.014474Z [DEBUG] logger.debug: DEBUG: Processing URL: magnet:?xt=urn:btih:RO3NJTH7KWG3YIZM6ACTLJOFMF4IXK64 +2026-02-01T04:47:02.617436Z [DEBUG] alldebrid.url_patterns: [alldebrid] url_patterns loaded 0 cached host domains; total patterns=2 +2026-02-01T04:47:19.322141Z [DEBUG] logger.debug: DEBUG: Provider alldebrid claimed magnet:?xt=urn:btih:RO3NJTH7KWG3YIZM6ACTLJOFMF4IXK64 +2026-02-01T04:47:35.912588Z [DEBUG] logger.debug: DEBUG: +2026-02-01T04:47:52.591623Z [DEBUG] logger.debug: DEBUG: +2026-02-01T04:48:09.205544Z [DEBUG] logger.debug: DEBUG: [search-file] Calling alldebrid.search(filters={}) +2026-02-01T04:48:25.895487Z [DEBUG] logger.debug: DEBUG: +2026-02-01T04:48:42.505354Z [DEBUG] logger.debug: DEBUG: +2026-02-01T04:48:59.158923Z [DEBUG] logger.debug: DEBUG: [search-file] alldebrid -> 1 result(s) +2026-02-01T04:49:15.817590Z [DEBUG] logger.debug: DEBUG: [alldebrid] Sent magnet 453036517 to AllDebrid for download +2026-02-01T04:49:32.492568Z [DEBUG] logger.debug: DEBUG: Provider alldebrid handled URL without file output +2026-02-01T04:49:49.187120Z [DEBUG] logger.debug: DEBUG: [download-file] Processing 0 piped item(s)... +2026-02-01T04:50:05.787087Z [DEBUG] download_file._run_impl: No downloads completed +2026-02-01T04:50:22.440968Z [DEBUG] logger.debug: DEBUG: [search-file] Calling alldebrid.search(filters={}) +2026-02-01T04:50:39.033859Z [DEBUG] logger.debug: DEBUG: +2026-02-01T04:50:55.684809Z [DEBUG] logger.debug: DEBUG: +2026-02-01T04:51:12.287393Z [DEBUG] logger.debug: DEBUG: [search-file] alldebrid -> 50 result(s)