hkjhjk
This commit is contained in:
@@ -847,8 +847,9 @@ local _last_ipc_error = ''
|
||||
local _last_ipc_last_req_json = ''
|
||||
local _last_ipc_last_resp_json = ''
|
||||
|
||||
-- Debounce helper start attempts (window in seconds)
|
||||
local _helper_start_debounce_ts = 0
|
||||
-- Debounce helper start attempts (window in seconds).
|
||||
-- Initialize below zero so the very first startup attempt is never rejected.
|
||||
local _helper_start_debounce_ts = -1000
|
||||
local HELPER_START_DEBOUNCE = 2.0
|
||||
|
||||
-- Track ready-heartbeat freshness so stale or non-timestamp values don't mask a stopped helper
|
||||
@@ -891,10 +892,12 @@ local function _is_pipeline_helper_ready()
|
||||
if age <= HELPER_READY_STALE_SECONDS then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- Fall back to the last time we observed a new value so stale data does not appear fresh.
|
||||
-- Fall back only for non-timestamp values so stale helper timestamps from a
|
||||
-- previous session do not look fresh right after Lua reload.
|
||||
if _helper_ready_last_seen_ts > 0 and (now - _helper_ready_last_seen_ts) <= HELPER_READY_STALE_SECONDS then
|
||||
return true
|
||||
end
|
||||
@@ -963,12 +966,17 @@ local function attempt_start_pipeline_helper_async(callback)
|
||||
|
||||
-- Debounce: don't spawn multiple helpers in quick succession
|
||||
local now = mp.get_time()
|
||||
if (now - _helper_start_debounce_ts) < HELPER_START_DEBOUNCE then
|
||||
if _helper_start_debounce_ts > -1 and (now - _helper_start_debounce_ts) < HELPER_START_DEBOUNCE then
|
||||
_lua_log('attempt_start_pipeline_helper_async: debounced (recent attempt)')
|
||||
callback(false)
|
||||
return
|
||||
end
|
||||
_helper_start_debounce_ts = now
|
||||
|
||||
-- Clear any stale ready heartbeat from an earlier helper instance before spawning.
|
||||
pcall(mp.set_property, PIPELINE_READY_PROP, '')
|
||||
_helper_ready_last_value = ''
|
||||
_helper_ready_last_seen_ts = 0
|
||||
|
||||
local python = _resolve_python_exe(true)
|
||||
if not python or python == '' then
|
||||
@@ -2137,7 +2145,8 @@ local function _capture_screenshot()
|
||||
end
|
||||
|
||||
if selected_store == '' then
|
||||
mp.osd_message('Select a store first (Store button)', 2)
|
||||
_pending_screenshot = { path = out_path }
|
||||
_open_store_picker_for_pending_screenshot()
|
||||
return
|
||||
end
|
||||
|
||||
@@ -2614,6 +2623,7 @@ _refresh_store_cache = function(timeout_seconds, on_complete)
|
||||
|
||||
local prev_count = (type(_cached_store_names) == 'table') and #_cached_store_names or 0
|
||||
local prev_key = _store_names_key(_cached_store_names)
|
||||
local had_previous = prev_count > 0
|
||||
|
||||
local cached_json = mp.get_property('user-data/medeia-store-choices-cached')
|
||||
_lua_log('stores: cache_read cached_json=' .. tostring(cached_json) .. ' len=' .. tostring(cached_json and #cached_json or 0))
|
||||
@@ -2632,8 +2642,15 @@ _refresh_store_cache = function(timeout_seconds, on_complete)
|
||||
out[#out + 1] = name
|
||||
end
|
||||
end
|
||||
if #out == 0 and had_previous then
|
||||
_lua_log('stores: ignoring empty cache payload; keeping previous store list')
|
||||
if type(on_complete) == 'function' then
|
||||
on_complete(true, false)
|
||||
end
|
||||
return true
|
||||
end
|
||||
_cached_store_names = out
|
||||
_store_cache_loaded = true
|
||||
_store_cache_loaded = (#out > 0) or _store_cache_loaded
|
||||
local preview = ''
|
||||
if #out > 0 then
|
||||
preview = table.concat(out, ', ')
|
||||
@@ -2694,15 +2711,21 @@ _refresh_store_cache = function(timeout_seconds, on_complete)
|
||||
out[#out + 1] = name
|
||||
end
|
||||
end
|
||||
_cached_store_names = out
|
||||
_store_cache_loaded = true
|
||||
local preview = ''
|
||||
if #out > 0 then
|
||||
preview = table.concat(out, ', ')
|
||||
if #out == 0 and had_previous then
|
||||
_lua_log('stores: helper returned empty store list; keeping previous store list')
|
||||
success = true
|
||||
changed = false
|
||||
else
|
||||
_cached_store_names = out
|
||||
_store_cache_loaded = (#out > 0) or _store_cache_loaded
|
||||
local preview = ''
|
||||
if #out > 0 then
|
||||
preview = table.concat(out, ', ')
|
||||
end
|
||||
_lua_log('stores: loaded ' .. tostring(#out) .. ' stores via helper request: ' .. tostring(preview))
|
||||
success = true
|
||||
changed = (#out ~= prev_count) or (_store_names_key(out) ~= prev_key)
|
||||
end
|
||||
_lua_log('stores: loaded ' .. tostring(#out) .. ' stores via helper request: ' .. tostring(preview))
|
||||
success = true
|
||||
changed = (#out ~= prev_count) or (_store_names_key(out) ~= prev_key)
|
||||
else
|
||||
_lua_log(
|
||||
'stores: failed to load store choices via helper; success='
|
||||
|
||||
@@ -63,7 +63,7 @@ if _ROOT not in sys.path:
|
||||
sys.path.insert(0, _ROOT)
|
||||
|
||||
from MPV.mpv_ipc import MPVIPCClient # noqa: E402
|
||||
from SYS.config import load_config # noqa: E402
|
||||
from SYS.config import load_config, reload_config # noqa: E402
|
||||
from SYS.logger import set_debug, debug, set_thread_stream # noqa: E402
|
||||
from SYS.repl_queue import enqueue_repl_command # noqa: E402
|
||||
from SYS.utils import format_bytes # noqa: E402
|
||||
@@ -80,6 +80,73 @@ _HELPER_LOG_BACKLOG_LIMIT = 200
|
||||
_ASYNC_PIPELINE_JOBS: Dict[str, Dict[str, Any]] = {}
|
||||
_ASYNC_PIPELINE_JOBS_LOCK = threading.Lock()
|
||||
_ASYNC_PIPELINE_JOB_TTL_SECONDS = 900.0
|
||||
_STORE_CHOICES_CACHE: list[str] = []
|
||||
_STORE_CHOICES_CACHE_LOCK = threading.Lock()
|
||||
|
||||
|
||||
def _normalize_store_choices(values: Any) -> list[str]:
|
||||
out: list[str] = []
|
||||
seen: set[str] = set()
|
||||
if not isinstance(values, (list, tuple, set)):
|
||||
return out
|
||||
for item in values:
|
||||
text = str(item or "").strip()
|
||||
if not text:
|
||||
continue
|
||||
key = text.casefold()
|
||||
if key in seen:
|
||||
continue
|
||||
seen.add(key)
|
||||
out.append(text)
|
||||
return sorted(out, key=str.casefold)
|
||||
|
||||
|
||||
def _get_cached_store_choices() -> list[str]:
|
||||
with _STORE_CHOICES_CACHE_LOCK:
|
||||
return list(_STORE_CHOICES_CACHE)
|
||||
|
||||
|
||||
def _set_cached_store_choices(choices: Any) -> list[str]:
|
||||
normalized = _normalize_store_choices(choices)
|
||||
with _STORE_CHOICES_CACHE_LOCK:
|
||||
_STORE_CHOICES_CACHE[:] = normalized
|
||||
return list(_STORE_CHOICES_CACHE)
|
||||
|
||||
|
||||
def _publish_store_choices_cache(ipc_path: str, choices: Any) -> None:
|
||||
cached = _normalize_store_choices(choices)
|
||||
if not ipc_path or not cached:
|
||||
return
|
||||
|
||||
payload = json.dumps(
|
||||
{
|
||||
"success": True,
|
||||
"choices": cached,
|
||||
},
|
||||
ensure_ascii=False,
|
||||
)
|
||||
|
||||
client = MPVIPCClient(socket_path=ipc_path, timeout=0.75, silent=True)
|
||||
try:
|
||||
client.send_command_no_wait(
|
||||
[
|
||||
"set_property_string",
|
||||
"user-data/medeia-store-choices-cached",
|
||||
payload,
|
||||
]
|
||||
)
|
||||
finally:
|
||||
try:
|
||||
client.disconnect()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def _load_store_choices_from_config(*, force_reload: bool = False) -> list[str]:
|
||||
from Store.registry import list_configured_backend_names # noqa: WPS433
|
||||
|
||||
cfg = reload_config() if force_reload else load_config()
|
||||
return _normalize_store_choices(list_configured_backend_names(cfg or {}))
|
||||
|
||||
|
||||
def _prune_async_pipeline_jobs(now: Optional[float] = None) -> None:
|
||||
@@ -632,17 +699,42 @@ def _run_op(op: str, data: Any) -> Dict[str, Any]:
|
||||
"store_choices",
|
||||
"get-store-choices",
|
||||
"get_store_choices"}:
|
||||
cached_choices = _get_cached_store_choices()
|
||||
refresh = False
|
||||
if isinstance(data, dict):
|
||||
refresh = bool(data.get("refresh") or data.get("reload"))
|
||||
|
||||
if cached_choices and not refresh:
|
||||
try:
|
||||
_publish_store_choices_cache(os.environ.get("MEDEIA_MPV_IPC", ""), cached_choices)
|
||||
except Exception:
|
||||
pass
|
||||
debug(f"[store-choices] using cached choices={len(cached_choices)}")
|
||||
return {
|
||||
"success": True,
|
||||
"stdout": "",
|
||||
"stderr": "",
|
||||
"error": None,
|
||||
"table": None,
|
||||
"choices": cached_choices,
|
||||
}
|
||||
|
||||
try:
|
||||
from SYS.config import reload_config # noqa: WPS433
|
||||
from Store import Store # noqa: WPS433
|
||||
|
||||
config_root = _runtime_config_root()
|
||||
cfg = reload_config()
|
||||
choices = _load_store_choices_from_config(force_reload=refresh)
|
||||
|
||||
storage = Store(config=cfg, suppress_debug=True)
|
||||
backends = storage.list_backends() or []
|
||||
choices = sorted({str(n)
|
||||
for n in backends if str(n).strip()})
|
||||
if not choices and cached_choices:
|
||||
choices = cached_choices
|
||||
debug(
|
||||
f"[store-choices] config returned empty; falling back to cached choices={len(choices)}"
|
||||
)
|
||||
|
||||
if choices:
|
||||
choices = _set_cached_store_choices(choices)
|
||||
try:
|
||||
_publish_store_choices_cache(os.environ.get("MEDEIA_MPV_IPC", ""), choices)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
debug(f"[store-choices] config_dir={config_root} choices={len(choices)}")
|
||||
|
||||
@@ -655,6 +747,22 @@ def _run_op(op: str, data: Any) -> Dict[str, Any]:
|
||||
"choices": choices,
|
||||
}
|
||||
except Exception as exc:
|
||||
if cached_choices:
|
||||
debug(
|
||||
f"[store-choices] refresh failed; returning cached choices={len(cached_choices)} error={type(exc).__name__}: {exc}"
|
||||
)
|
||||
try:
|
||||
_publish_store_choices_cache(os.environ.get("MEDEIA_MPV_IPC", ""), cached_choices)
|
||||
except Exception:
|
||||
pass
|
||||
return {
|
||||
"success": True,
|
||||
"stdout": "",
|
||||
"stderr": "",
|
||||
"error": None,
|
||||
"table": None,
|
||||
"choices": cached_choices,
|
||||
}
|
||||
return {
|
||||
"success": False,
|
||||
"stdout": "",
|
||||
@@ -1529,6 +1637,7 @@ def main(argv: Optional[list[str]] = None) -> int:
|
||||
dict) else None
|
||||
)
|
||||
if isinstance(startup_choices, list):
|
||||
startup_choices = _set_cached_store_choices(startup_choices)
|
||||
preview = ", ".join(str(x) for x in startup_choices[:50])
|
||||
_append_helper_log(
|
||||
f"[helper] startup store-choices count={len(startup_choices)} items={preview}"
|
||||
@@ -1536,20 +1645,7 @@ def main(argv: Optional[list[str]] = None) -> int:
|
||||
|
||||
# Publish to a cached property for Lua to read without IPC request.
|
||||
try:
|
||||
cached_json = json.dumps(
|
||||
{
|
||||
"success": True,
|
||||
"choices": startup_choices
|
||||
},
|
||||
ensure_ascii=False
|
||||
)
|
||||
client.send_command_no_wait(
|
||||
[
|
||||
"set_property_string",
|
||||
"user-data/medeia-store-choices-cached",
|
||||
cached_json
|
||||
]
|
||||
)
|
||||
_publish_store_choices_cache(str(args.ipc), startup_choices)
|
||||
_append_helper_log(
|
||||
"[helper] published store-choices to user-data/medeia-store-choices-cached"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user