This commit is contained in:
2026-03-25 00:56:58 -07:00
parent 96f327e4dc
commit c31402c8f1
5 changed files with 89 additions and 12 deletions

View File

@@ -396,6 +396,9 @@ M._reset_uosc_input_state = function(reason)
return false return false
end end
pcall(mp.commandv, 'script-message-to', 'uosc', 'close-menu') pcall(mp.commandv, 'script-message-to', 'uosc', 'close-menu')
pcall(mp.commandv, 'script-message-to', 'uosc', 'sync-cursor')
M._disable_input_section('input_console', why .. '@immediate')
M._disable_input_section('input_forced_console', why .. '@immediate')
mp.add_timeout(0.05, function() mp.add_timeout(0.05, function()
if ensure_uosc_loaded() then if ensure_uosc_loaded() then
pcall(mp.commandv, 'script-message-to', 'uosc', 'sync-cursor') pcall(mp.commandv, 'script-message-to', 'uosc', 'sync-cursor')
@@ -403,6 +406,13 @@ M._reset_uosc_input_state = function(reason)
M._disable_input_section('input_console', why .. '@sync') M._disable_input_section('input_console', why .. '@sync')
M._disable_input_section('input_forced_console', why .. '@sync') M._disable_input_section('input_forced_console', why .. '@sync')
end) end)
mp.add_timeout(0.20, function()
if ensure_uosc_loaded() then
pcall(mp.commandv, 'script-message-to', 'uosc', 'sync-cursor')
end
M._disable_input_section('input_console', why .. '@sync2')
M._disable_input_section('input_forced_console', why .. '@sync2')
end)
return true return true
end end
@@ -1332,9 +1342,10 @@ local function attempt_start_pipeline_helper_async(callback)
end end
-- Wait for helper to become ready in background (non-blocking). -- Wait for helper to become ready in background (non-blocking).
-- 12 s gives Python time to kill a stale lock holder (PS scan + taskkill) -- The Python helper can spend up to 12s recovering a stale singleton lock
-- and publish its first ready heartbeat before we give up. -- before it even starts connecting to mpv IPC, so the Lua-side wait must be
local deadline = mp.get_time() + 12.0 -- comfortably longer than that to avoid false startup failures.
local deadline = mp.get_time() + 45.0
local timer local timer
timer = mp.add_periodic_timer(0.1, function() timer = mp.add_periodic_timer(0.1, function()
if _is_pipeline_helper_ready() then if _is_pipeline_helper_ready() then
@@ -1345,7 +1356,7 @@ local function attempt_start_pipeline_helper_async(callback)
end end
if mp.get_time() >= deadline then if mp.get_time() >= deadline then
timer:kill() timer:kill()
_lua_log('attempt_start_pipeline_helper_async: timeout waiting for ready') _lua_log('attempt_start_pipeline_helper_async: timeout waiting for ready ' .. _helper_ready_diagnostics())
-- Reset debounce so the next attempt is not immediate; gives the -- Reset debounce so the next attempt is not immediate; gives the
-- still-running Python helper time to die or acquire the lock. -- still-running Python helper time to die or acquire the lock.
_helper_start_debounce_ts = mp.get_time() _helper_start_debounce_ts = mp.get_time()
@@ -3795,12 +3806,11 @@ local function _sync_current_web_url_from_playback()
end end
end end
mp.add_hook('on_load', 50, function(hook) mp.add_hook('on_load', 50, function()
local ok, err = pcall(M._apply_web_subtitle_load_defaults, 'on_load') local ok, err = pcall(M._apply_web_subtitle_load_defaults, 'on_load')
if not ok then if not ok then
_lua_log('web-subtitles: on_load setup failed err=' .. tostring(err)) _lua_log('web-subtitles: on_load setup failed err=' .. tostring(err))
end end
hook:continue()
end) end)
local _current_store_url_status = { local _current_store_url_status = {

View File

@@ -1120,7 +1120,7 @@ def _run_op(op: str, data: Any) -> Dict[str, Any]:
def _append_helper_log(text: str) -> None: def _append_helper_log(text: str) -> None:
"""Log to database instead of file. This provides unified logging with rest of system.""" """Log helper diagnostics to file, database, and the mpv console emitter."""
payload = (text or "").rstrip() payload = (text or "").rstrip()
if not payload: if not payload:
return return
@@ -1129,6 +1129,12 @@ def _append_helper_log(text: str) -> None:
if len(_HELPER_LOG_BACKLOG) > _HELPER_LOG_BACKLOG_LIMIT: if len(_HELPER_LOG_BACKLOG) > _HELPER_LOG_BACKLOG_LIMIT:
del _HELPER_LOG_BACKLOG[:-_HELPER_LOG_BACKLOG_LIMIT] del _HELPER_LOG_BACKLOG[:-_HELPER_LOG_BACKLOG_LIMIT]
try:
with open(_helper_log_path(), "a", encoding="utf-8", errors="replace") as fh:
fh.write(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] {payload}\n")
except Exception:
pass
try: try:
# Try database logging first (best practice: unified logging) # Try database logging first (best practice: unified logging)
from SYS.database import log_to_db from SYS.database import log_to_db

View File

@@ -1113,6 +1113,13 @@ mp.register_script_message('close-menu', function(type)
if Menu:is_open(type) then Menu:close() end if Menu:is_open(type) then Menu:close() end
end) end)
mp.register_script_message('sync-cursor', function() mp.register_script_message('sync-cursor', function()
cursor.last_events.primary_down = nil
cursor.last_events.primary_up = nil
cursor.last_events.secondary_down = nil
cursor.last_events.secondary_up = nil
cursor.last_events.primary_click = nil
cursor.last_events.secondary_click = nil
cursor.history:clear()
local mouse = mp.get_property_native('mouse-pos') local mouse = mp.get_property_native('mouse-pos')
if type(mouse) == 'table' and mouse.hover and mouse.x and mouse.y then if type(mouse) == 'table' and mouse.hover and mouse.x and mouse.y then
cursor:move(mouse.x, mouse.y) cursor:move(mouse.x, mouse.y)

View File

@@ -2221,6 +2221,21 @@ class ItemDetailView(Table):
details_table.add_column("Key", style="cyan", justify="right", width=15) details_table.add_column("Key", style="cyan", justify="right", width=15)
details_table.add_column("Value", style="white") details_table.add_column("Value", style="white")
def _render_tag_text(tag_value: Any) -> Text:
tag_text = Text()
tag_text.append("#", style="dim")
raw = str(tag_value or "")
namespace, sep, value = raw.partition(":")
if sep and namespace:
tag_text.append(namespace, style="#6ecbff")
tag_text.append(sep, style="#6ecbff")
if value:
tag_text.append(value, style="green")
else:
tag_text.append(raw, style="green")
return tag_text
# Canonical display order for metadata # Canonical display order for metadata
order = ["Title", "Hash", "Store", "Path", "Ext", "Size", "Duration", "Url", "Relations"] order = ["Title", "Hash", "Store", "Path", "Ext", "Size", "Duration", "Url", "Relations"]
@@ -2268,7 +2283,7 @@ class ItemDetailView(Table):
if isinstance(tags, str): if isinstance(tags, str):
tags = [t.strip() for t in tags.split(",") if t.strip()] tags = [t.strip() for t in tags.split(",") if t.strip()]
tags_sorted = sorted(map(str, tags)) tags_sorted = sorted(map(str, tags))
tag_cols = Columns([f"[dim]#[/dim]{t}" for t in tags_sorted], equal=True, expand=True) tag_cols = Columns([_render_tag_text(t) for t in tags_sorted], equal=True, expand=True)
details_table.add_row("", "") # Spacer details_table.add_row("", "") # Spacer
details_table.add_row("Tags:", tag_cols) details_table.add_row("Tags:", tag_cols)
has_details = True has_details = True

View File

@@ -256,18 +256,41 @@ def _focus_db_log_rows(
return _collapse_repeated_log_lines(rendered) return _collapse_repeated_log_lines(rendered)
def _slice_mpv_log_to_latest_run(lines: Sequence[str]) -> List[str]: def _slice_log_to_latest_marker(
startup_pattern = re.compile(r"\bmpv v\d", re.IGNORECASE) lines: Sequence[str],
patterns: Sequence[re.Pattern[str]],
) -> List[str]:
collected = list(lines) collected = list(lines)
if not collected: if not collected:
return [] return []
for idx in range(len(collected) - 1, -1, -1): for idx in range(len(collected) - 1, -1, -1):
text = str(collected[idx] or "") text = str(collected[idx] or "")
if startup_pattern.search(text): if any(pattern.search(text) for pattern in patterns):
return collected[idx:] return collected[idx:]
return collected return collected
def _slice_mpv_log_to_latest_run(lines: Sequence[str]) -> List[str]:
return _slice_log_to_latest_marker(
lines,
[re.compile(r"\bmpv v\d", re.IGNORECASE)],
)
def _slice_lua_log_to_latest_run(lines: Sequence[str]) -> List[str]:
return _slice_log_to_latest_marker(
lines,
[re.compile(r"medeia[- ]lua loaded version=", re.IGNORECASE)],
)
def _slice_helper_log_to_latest_run(lines: Sequence[str]) -> List[str]:
return _slice_log_to_latest_marker(
lines,
[re.compile(r"\[helper\] version=.* started ipc=", re.IGNORECASE)],
)
def _get_mpv_property(prop_name: str) -> Optional[Any]: def _get_mpv_property(prop_name: str) -> Optional[Any]:
try: try:
resp = _send_ipc_command( resp = _send_ipc_command(
@@ -2456,8 +2479,22 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
else: else:
print("MPV core log: <no focused entries>") print("MPV core log: <no focused entries>")
try:
lua_tail = _tail_text_file(str(_lua_log_file()), max_lines=400, max_bytes=262144)
except Exception:
lua_tail = []
lua_tail = _slice_lua_log_to_latest_run(lua_tail)
lua_tail = _collapse_repeated_log_lines(_apply_log_filter(lua_tail, log_filter_text))
if lua_tail:
title = "Medeia Lua log (latest run)"
if log_filter_text:
title += f" filtered by '{log_filter_text}'"
title += ":"
print(title)
for line in lua_tail:
print(line)
fallback_logs = [ fallback_logs = [
("Medeia Lua log file tail", str(_lua_log_file())),
("Medeia helper log file tail", str(_helper_log_file())), ("Medeia helper log file tail", str(_helper_log_file())),
] ]
for title, path in fallback_logs: for title, path in fallback_logs:
@@ -2465,6 +2502,8 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
lines = _tail_text_file(path, max_lines=120) lines = _tail_text_file(path, max_lines=120)
except Exception: except Exception:
lines = [] lines = []
if path == str(_helper_log_file()):
lines = _slice_helper_log_to_latest_run(lines)
lines = _collapse_repeated_log_lines(_apply_log_filter(lines, log_filter_text)) lines = _collapse_repeated_log_lines(_apply_log_filter(lines, log_filter_text))
if not lines: if not lines:
continue continue