hkjhjk
This commit is contained in:
@@ -847,8 +847,9 @@ local _last_ipc_error = ''
|
|||||||
local _last_ipc_last_req_json = ''
|
local _last_ipc_last_req_json = ''
|
||||||
local _last_ipc_last_resp_json = ''
|
local _last_ipc_last_resp_json = ''
|
||||||
|
|
||||||
-- Debounce helper start attempts (window in seconds)
|
-- Debounce helper start attempts (window in seconds).
|
||||||
local _helper_start_debounce_ts = 0
|
-- Initialize below zero so the very first startup attempt is never rejected.
|
||||||
|
local _helper_start_debounce_ts = -1000
|
||||||
local HELPER_START_DEBOUNCE = 2.0
|
local HELPER_START_DEBOUNCE = 2.0
|
||||||
|
|
||||||
-- Track ready-heartbeat freshness so stale or non-timestamp values don't mask a stopped helper
|
-- 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
|
if age <= HELPER_READY_STALE_SECONDS then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
return false
|
||||||
end
|
end
|
||||||
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
|
if _helper_ready_last_seen_ts > 0 and (now - _helper_ready_last_seen_ts) <= HELPER_READY_STALE_SECONDS then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
@@ -963,13 +966,18 @@ local function attempt_start_pipeline_helper_async(callback)
|
|||||||
|
|
||||||
-- Debounce: don't spawn multiple helpers in quick succession
|
-- Debounce: don't spawn multiple helpers in quick succession
|
||||||
local now = mp.get_time()
|
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)')
|
_lua_log('attempt_start_pipeline_helper_async: debounced (recent attempt)')
|
||||||
callback(false)
|
callback(false)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
_helper_start_debounce_ts = now
|
_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)
|
local python = _resolve_python_exe(true)
|
||||||
if not python or python == '' then
|
if not python or python == '' then
|
||||||
_lua_log('attempt_start_pipeline_helper_async: no python executable available')
|
_lua_log('attempt_start_pipeline_helper_async: no python executable available')
|
||||||
@@ -2137,7 +2145,8 @@ local function _capture_screenshot()
|
|||||||
end
|
end
|
||||||
|
|
||||||
if selected_store == '' then
|
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
|
return
|
||||||
end
|
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_count = (type(_cached_store_names) == 'table') and #_cached_store_names or 0
|
||||||
local prev_key = _store_names_key(_cached_store_names)
|
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')
|
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))
|
_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
|
out[#out + 1] = name
|
||||||
end
|
end
|
||||||
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
|
_cached_store_names = out
|
||||||
_store_cache_loaded = true
|
_store_cache_loaded = (#out > 0) or _store_cache_loaded
|
||||||
local preview = ''
|
local preview = ''
|
||||||
if #out > 0 then
|
if #out > 0 then
|
||||||
preview = table.concat(out, ', ')
|
preview = table.concat(out, ', ')
|
||||||
@@ -2694,8 +2711,13 @@ _refresh_store_cache = function(timeout_seconds, on_complete)
|
|||||||
out[#out + 1] = name
|
out[#out + 1] = name
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
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
|
_cached_store_names = out
|
||||||
_store_cache_loaded = true
|
_store_cache_loaded = (#out > 0) or _store_cache_loaded
|
||||||
local preview = ''
|
local preview = ''
|
||||||
if #out > 0 then
|
if #out > 0 then
|
||||||
preview = table.concat(out, ', ')
|
preview = table.concat(out, ', ')
|
||||||
@@ -2703,6 +2725,7 @@ _refresh_store_cache = function(timeout_seconds, on_complete)
|
|||||||
_lua_log('stores: loaded ' .. tostring(#out) .. ' stores via helper request: ' .. tostring(preview))
|
_lua_log('stores: loaded ' .. tostring(#out) .. ' stores via helper request: ' .. tostring(preview))
|
||||||
success = true
|
success = true
|
||||||
changed = (#out ~= prev_count) or (_store_names_key(out) ~= prev_key)
|
changed = (#out ~= prev_count) or (_store_names_key(out) ~= prev_key)
|
||||||
|
end
|
||||||
else
|
else
|
||||||
_lua_log(
|
_lua_log(
|
||||||
'stores: failed to load store choices via helper; success='
|
'stores: failed to load store choices via helper; success='
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ if _ROOT not in sys.path:
|
|||||||
sys.path.insert(0, _ROOT)
|
sys.path.insert(0, _ROOT)
|
||||||
|
|
||||||
from MPV.mpv_ipc import MPVIPCClient # noqa: E402
|
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.logger import set_debug, debug, set_thread_stream # noqa: E402
|
||||||
from SYS.repl_queue import enqueue_repl_command # noqa: E402
|
from SYS.repl_queue import enqueue_repl_command # noqa: E402
|
||||||
from SYS.utils import format_bytes # 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: Dict[str, Dict[str, Any]] = {}
|
||||||
_ASYNC_PIPELINE_JOBS_LOCK = threading.Lock()
|
_ASYNC_PIPELINE_JOBS_LOCK = threading.Lock()
|
||||||
_ASYNC_PIPELINE_JOB_TTL_SECONDS = 900.0
|
_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:
|
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",
|
"store_choices",
|
||||||
"get-store-choices",
|
"get-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:
|
try:
|
||||||
from SYS.config import reload_config # noqa: WPS433
|
_publish_store_choices_cache(os.environ.get("MEDEIA_MPV_IPC", ""), cached_choices)
|
||||||
from Store import Store # noqa: WPS433
|
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:
|
||||||
config_root = _runtime_config_root()
|
config_root = _runtime_config_root()
|
||||||
cfg = reload_config()
|
choices = _load_store_choices_from_config(force_reload=refresh)
|
||||||
|
|
||||||
storage = Store(config=cfg, suppress_debug=True)
|
if not choices and cached_choices:
|
||||||
backends = storage.list_backends() or []
|
choices = cached_choices
|
||||||
choices = sorted({str(n)
|
debug(
|
||||||
for n in backends if str(n).strip()})
|
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)}")
|
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,
|
"choices": choices,
|
||||||
}
|
}
|
||||||
except Exception as exc:
|
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 {
|
return {
|
||||||
"success": False,
|
"success": False,
|
||||||
"stdout": "",
|
"stdout": "",
|
||||||
@@ -1529,6 +1637,7 @@ def main(argv: Optional[list[str]] = None) -> int:
|
|||||||
dict) else None
|
dict) else None
|
||||||
)
|
)
|
||||||
if isinstance(startup_choices, list):
|
if isinstance(startup_choices, list):
|
||||||
|
startup_choices = _set_cached_store_choices(startup_choices)
|
||||||
preview = ", ".join(str(x) for x in startup_choices[:50])
|
preview = ", ".join(str(x) for x in startup_choices[:50])
|
||||||
_append_helper_log(
|
_append_helper_log(
|
||||||
f"[helper] startup store-choices count={len(startup_choices)} items={preview}"
|
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.
|
# Publish to a cached property for Lua to read without IPC request.
|
||||||
try:
|
try:
|
||||||
cached_json = json.dumps(
|
_publish_store_choices_cache(str(args.ipc), startup_choices)
|
||||||
{
|
|
||||||
"success": True,
|
|
||||||
"choices": startup_choices
|
|
||||||
},
|
|
||||||
ensure_ascii=False
|
|
||||||
)
|
|
||||||
client.send_command_no_wait(
|
|
||||||
[
|
|
||||||
"set_property_string",
|
|
||||||
"user-data/medeia-store-choices-cached",
|
|
||||||
cached_json
|
|
||||||
]
|
|
||||||
)
|
|
||||||
_append_helper_log(
|
_append_helper_log(
|
||||||
"[helper] published store-choices to user-data/medeia-store-choices-cached"
|
"[helper] published store-choices to user-data/medeia-store-choices-cached"
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user