diff --git a/MPV/LUA/main.lua b/MPV/LUA/main.lua index 8becba2..cf8a46f 100644 --- a/MPV/LUA/main.lua +++ b/MPV/LUA/main.lua @@ -396,6 +396,9 @@ M._reset_uosc_input_state = function(reason) return false end 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() if ensure_uosc_loaded() then 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_forced_console', why .. '@sync') 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 end @@ -1332,9 +1342,10 @@ local function attempt_start_pipeline_helper_async(callback) end -- Wait for helper to become ready in background (non-blocking). - -- 12 s gives Python time to kill a stale lock holder (PS scan + taskkill) - -- and publish its first ready heartbeat before we give up. - local deadline = mp.get_time() + 12.0 + -- The Python helper can spend up to 12s recovering a stale singleton lock + -- before it even starts connecting to mpv IPC, so the Lua-side wait must be + -- comfortably longer than that to avoid false startup failures. + local deadline = mp.get_time() + 45.0 local timer timer = mp.add_periodic_timer(0.1, function() if _is_pipeline_helper_ready() then @@ -1345,7 +1356,7 @@ local function attempt_start_pipeline_helper_async(callback) end if mp.get_time() >= deadline then 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 -- still-running Python helper time to die or acquire the lock. _helper_start_debounce_ts = mp.get_time() @@ -3795,12 +3806,11 @@ local function _sync_current_web_url_from_playback() 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') if not ok then _lua_log('web-subtitles: on_load setup failed err=' .. tostring(err)) end - hook:continue() end) local _current_store_url_status = { diff --git a/MPV/pipeline_helper.py b/MPV/pipeline_helper.py index 1e40667..8c803a1 100644 --- a/MPV/pipeline_helper.py +++ b/MPV/pipeline_helper.py @@ -1120,7 +1120,7 @@ def _run_op(op: str, data: Any) -> Dict[str, Any]: 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() if not payload: return @@ -1129,6 +1129,12 @@ def _append_helper_log(text: str) -> None: if len(_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 database logging first (best practice: unified logging) from SYS.database import log_to_db diff --git a/MPV/portable_config/scripts/uosc/scripts/uosc/main.lua b/MPV/portable_config/scripts/uosc/scripts/uosc/main.lua index 4a9bac8..df6e5a0 100644 --- a/MPV/portable_config/scripts/uosc/scripts/uosc/main.lua +++ b/MPV/portable_config/scripts/uosc/scripts/uosc/main.lua @@ -1113,6 +1113,13 @@ mp.register_script_message('close-menu', function(type) if Menu:is_open(type) then Menu:close() end end) 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') if type(mouse) == 'table' and mouse.hover and mouse.x and mouse.y then cursor:move(mouse.x, mouse.y) diff --git a/SYS/result_table.py b/SYS/result_table.py index 9349e36..60d6484 100644 --- a/SYS/result_table.py +++ b/SYS/result_table.py @@ -2221,6 +2221,21 @@ class ItemDetailView(Table): details_table.add_column("Key", style="cyan", justify="right", width=15) 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 order = ["Title", "Hash", "Store", "Path", "Ext", "Size", "Duration", "Url", "Relations"] @@ -2268,7 +2283,7 @@ class ItemDetailView(Table): if isinstance(tags, str): tags = [t.strip() for t in tags.split(",") if t.strip()] 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("Tags:", tag_cols) has_details = True diff --git a/cmdnat/pipe.py b/cmdnat/pipe.py index 94cca7c..d3da319 100644 --- a/cmdnat/pipe.py +++ b/cmdnat/pipe.py @@ -256,18 +256,41 @@ def _focus_db_log_rows( return _collapse_repeated_log_lines(rendered) -def _slice_mpv_log_to_latest_run(lines: Sequence[str]) -> List[str]: - startup_pattern = re.compile(r"\bmpv v\d", re.IGNORECASE) +def _slice_log_to_latest_marker( + lines: Sequence[str], + patterns: Sequence[re.Pattern[str]], +) -> List[str]: collected = list(lines) if not collected: return [] for idx in range(len(collected) - 1, -1, -1): text = str(collected[idx] or "") - if startup_pattern.search(text): + if any(pattern.search(text) for pattern in patterns): return collected[idx:] 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]: try: resp = _send_ipc_command( @@ -2456,8 +2479,22 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int: else: print("MPV core log: ") + 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 = [ - ("Medeia Lua log file tail", str(_lua_log_file())), ("Medeia helper log file tail", str(_helper_log_file())), ] 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) except Exception: 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)) if not lines: continue