diff --git a/SYS/result_table.py b/SYS/result_table.py index e1c4b48..b758616 100644 --- a/SYS/result_table.py +++ b/SYS/result_table.py @@ -15,6 +15,7 @@ from __future__ import annotations from dataclasses import dataclass, field from typing import Any, Dict, List, Optional, Callable, Set, Tuple from pathlib import Path +from urllib.parse import urlparse import json import re @@ -2546,6 +2547,44 @@ class ItemDetailView(Table): return any(_has_renderable_value(item) for item in value) return True + def _looks_like_http_url(value: Any) -> bool: + text = str(value or "").strip().lower() + return text.startswith("http://") or text.startswith("https://") + + def _short_link_label(url_value: str, *, max_len: int = 68) -> str: + text = str(url_value or "").strip() + if not text: + return "" + + parsed = urlparse(text) + host = (parsed.netloc or "").strip() + path = (parsed.path or "").strip() + leaf = path.rstrip("/").split("/")[-1] if path else "" + + if host and leaf: + label = f"{host}/.../{leaf}" + elif host and path: + label = f"{host}{path}" + elif host: + label = host + else: + label = text + + if parsed.query: + label = f"{label}?..." + + if len(label) > max_len: + return f"{label[:max_len - 3]}..." + return label + + def _render_detail_value(key: str, value: Any) -> Any: + # Keep terminal links compact while preserving full click target. + if str(key or "").strip().lower() in {"path", "url"} and _looks_like_http_url(value): + full_url = str(value).strip() + label = _short_link_label(full_url) + return _rich().Text(label, style=f"underline cyan link {full_url}") + return str(value) + # Canonical display order for metadata; plugin-specific detail views can # prepend a preferred order without needing to reimplement rendering. order: List[str] = [] @@ -2580,7 +2619,7 @@ class ItemDetailView(Table): val = "\n".join([f"[dim]→[/dim] {r}" for r in val]) if _has_renderable_value(val): - details_table.add_row(f"{key}:", str(val)) + details_table.add_row(f"{key}:", _render_detail_value(key, val)) has_details = True # Add any remaining metadata not in the canonical list diff --git a/plugins/mpv/LUA/main.lua b/plugins/mpv/LUA/main.lua index 0a53b88..03ad71a 100644 --- a/plugins/mpv/LUA/main.lua +++ b/plugins/mpv/LUA/main.lua @@ -4,7 +4,7 @@ local msg = require 'mp.msg' local M = {} -local MEDEIA_LUA_VERSION = '2026-03-23.1' +local MEDEIA_LUA_VERSION = '2026-06-25.3' -- Expose a tiny breadcrumb for debugging which script version is loaded. pcall(mp.set_property, 'user-data/medeia-lua-version', MEDEIA_LUA_VERSION) @@ -4164,6 +4164,10 @@ function M._build_web_ytdl_raw_options() if not lower:find('sub%-langs=', 1) then extra[#extra + 1] = 'sub-langs=[en.*,en,-live_chat]' end + if not lower:find('sub%-format=', 1) then + -- Prefer chunked subtitle formats over word-by-word JSON tracks. + extra[#extra + 1] = 'sub-format=srt/vtt/best' + end if #extra == 0 then return raw ~= '' and raw or nil @@ -4174,15 +4178,43 @@ function M._build_web_ytdl_raw_options() return table.concat(extra, ',') end -function M._apply_web_subtitle_load_defaults(reason) - local target = trim(tostring(mp.get_property('path') or mp.get_property('stream-open-filename') or '')) - if target == '' or not _is_http_url(target) then +function M._prime_web_subtitle_global_defaults(reason) + local raw = M._build_web_ytdl_raw_options() + if not raw or raw == '' then return false end - if not _is_ytdlp_url(target) then + pcall(mp.set_property, 'options/ytdl-raw-options', raw) + pcall(mp.set_property, 'ytdl-raw-options', raw) + _lua_log('web-subtitles: primed global ytdl defaults reason=' .. tostring(reason or 'startup')) + return true +end + +function M._resolve_web_subtitle_target(preferred_target) + local explicit = trim(tostring(preferred_target or '')) + if explicit ~= '' and _is_http_url(explicit) and _is_ytdlp_url(explicit) then + return explicit + end + + local path_target = trim(tostring(mp.get_property('path') or mp.get_property('stream-open-filename') or '')) + if path_target ~= '' and _is_http_url(path_target) and _is_ytdlp_url(path_target) then + return path_target + end + + local current = _get_current_web_url() + if current and current ~= '' and _is_http_url(current) and _is_ytdlp_url(current) then + return current + end + + return nil +end + +function M._apply_web_subtitle_load_defaults(reason, preferred_target) + local target = M._resolve_web_subtitle_target(preferred_target) + if not target then return false end + _set_current_web_url(target) M._prepare_ytdl_format_for_web_load(target, reason or 'on-load') local raw = M._build_web_ytdl_raw_options() @@ -4192,6 +4224,8 @@ function M._apply_web_subtitle_load_defaults(reason) pcall(mp.set_property, 'file-local-options/sub-visibility', 'yes') pcall(mp.set_property, 'file-local-options/sid', 'auto') pcall(mp.set_property, 'file-local-options/track-auto-selection', 'yes') + -- Strip ASS positioning/styling for web subtitles to keep lines anchored naturally. + pcall(mp.set_property, 'file-local-options/sub-ass-override', 'strip') _lua_log('web-subtitles: prepared load defaults reason=' .. tostring(reason or 'on-load') .. ' target=' .. tostring(target)) return true end @@ -5554,6 +5588,10 @@ local function _apply_ytdl_format_and_reload(url, fmt) _lua_log('change-format: setting ytdl format=' .. tostring(fmt)) _skip_next_store_check_url = _normalize_url_for_store_lookup(url) _set_current_web_url(url) + if _is_ytdlp_url(url) then + M._prime_web_subtitle_global_defaults('change-format') + M._apply_web_subtitle_load_defaults('change-format', url) + end M._remember_ytdl_download_format(url, fmt) pcall(mp.set_property, 'options/ytdl-format', tostring(fmt)) pcall(mp.set_property, 'file-local-options/ytdl-format', tostring(fmt)) @@ -6559,6 +6597,10 @@ mp.register_script_message('medios-load-url-event', function(json) _log_all('INFO', 'Load URL started: ' .. url) _lua_log('[LOAD-URL] Starting to load: ' .. url) _set_current_web_url(url) + if _is_ytdlp_url(url) then + M._prime_web_subtitle_global_defaults('load-url') + M._apply_web_subtitle_load_defaults('load-url', url) + end _pending_format_change = nil pcall(mp.set_property, 'options/ytdl-format', '') pcall(mp.set_property, 'file-local-options/ytdl-format', '') @@ -6756,6 +6798,10 @@ end) mp.add_timeout(0, function() pcall(ensure_mpv_ipc_server) pcall(_lua_log, 'medeia-lua loaded version=' .. MEDEIA_LUA_VERSION) + local ok_subtitle_defaults, subtitle_defaults_err = pcall(M._prime_web_subtitle_global_defaults, 'startup') + if not ok_subtitle_defaults then + _lua_log('web-subtitles: startup defaults failed err=' .. tostring(subtitle_defaults_err)) + end local ok_helper, helper_err = pcall(function() attempt_start_pipeline_helper_async(function(success) diff --git a/scripts/bootstrap.py b/scripts/bootstrap.py index 6942cea..54a578d 100644 --- a/scripts/bootstrap.py +++ b/scripts/bootstrap.py @@ -1790,8 +1790,10 @@ if (Test-Path (Join-Path $repo ".git")) { $updateText = ($update | Out-String) $repoUpdated = ($updateText -match "Updating|Fast-forward") $repoUpToDate = ($updateText -match "Already up[\s-]+to[\s-]+date") + $skipDepUpdate = $false if ($gitExit -ne 0) { + $skipDepUpdate = $true Write-Host "[mm] Warning: git update check failed; continuing startup." -ForegroundColor Yellow $gitTail = @($update | Select-Object -Last 3) foreach ($line in $gitTail) { @@ -1805,7 +1807,9 @@ if (Test-Path (Join-Path $repo ".git")) { Write-Host "[mm] Repository update check completed." -ForegroundColor DarkGray } - if (-not $env:MM_NO_DEP_UPDATE -and (Test-Path $py) -and (Test-Path $requirements)) { + if ($skipDepUpdate) { + Write-Host "[mm] Python module update skipped (network/update check unavailable)." -ForegroundColor DarkGray + } elseif (-not $env:MM_NO_DEP_UPDATE -and (Test-Path $py) -and (Test-Path $requirements)) { Write-Host "[mm] Checking Python module updates..." -ForegroundColor DarkGray $depOutput = & $py -m pip install --disable-pip-version-check --upgrade -r $requirements 2>&1 $depExit = $LASTEXITCODE @@ -1905,6 +1909,7 @@ if (Test-Path (Join-Path $repo 'CLI.py')) { " git -C \"!REPO!\" pull --ff-only >\"!GIT_OUT!\" 2>&1\n" " set \"GIT_EXIT=!errorlevel!\"\n" " set \"REPO_UPDATED=false\"\n" + " set \"SKIP_DEP_UPDATE=false\"\n" " if \"!GIT_EXIT!\" == \"0\" (\n" " findstr /i /c:\"Updating\" /c:\"Fast-forward\" \"!GIT_OUT!\" >nul 2>&1\n" " if !errorlevel! == 0 (\n" @@ -1914,11 +1919,14 @@ if (Test-Path (Join-Path $repo 'CLI.py')) { " echo [mm] Repository already up to date.\n" " )\n" " ) else (\n" + " set \"SKIP_DEP_UPDATE=true\"\n" " echo [mm] Warning: git update check failed; continuing startup.\n" " type \"!GIT_OUT!\"\n" " )\n" " del /q \"!GIT_OUT!\" >nul 2>&1\n" - " if not defined MM_NO_DEP_UPDATE (\n" + " if \"!SKIP_DEP_UPDATE!\" == \"true\" (\n" + " echo [mm] Python module update skipped (network/update check unavailable).\n" + " ) else if not defined MM_NO_DEP_UPDATE (\n" " if exist \"!PY!\" if exist \"!REQ!\" (\n" " echo [mm] Checking Python module updates...\n" " set \"PIP_OUT=%TEMP%\\mm_pip_update_!RANDOM!_!RANDOM!.log\"\n" @@ -2127,7 +2135,9 @@ if (Test-Path (Join-Path $repo 'CLI.py')) { ' UPDATE_OUT=$(git -C "$REPO" pull --ff-only 2>&1)\n' ' UPDATE_EXIT=$?\n' ' REPO_UPDATED="false"\n' + ' SKIP_DEP_UPDATE="false"\n' ' if [ $UPDATE_EXIT -ne 0 ]; then\n' + ' SKIP_DEP_UPDATE="true"\n' ' echo "[mm] Warning: git update check failed; continuing startup."\n' ' printf "%s\n" "$UPDATE_OUT" | tail -n 3\n' ' elif echo "$UPDATE_OUT" | grep -qiE \'Updating|Fast-forward\'; then\n' @@ -2144,7 +2154,9 @@ if (Test-Path (Join-Path $repo 'CLI.py')) { ' elif [ -x "$VENV/bin/python" ]; then\n' ' MM_PY="$VENV/bin/python"\n' ' fi\n' - ' if [ -z "${MM_NO_DEP_UPDATE:-}" ] && [ -n "$MM_PY" ] && [ -f "$REPO/scripts/requirements.txt" ]; then\n' + ' if [ "$SKIP_DEP_UPDATE" = "true" ]; then\n' + ' echo "[mm] Python module update skipped (network/update check unavailable)."\n' + ' elif [ -z "${MM_NO_DEP_UPDATE:-}" ] && [ -n "$MM_PY" ] && [ -f "$REPO/scripts/requirements.txt" ]; then\n' ' echo "[mm] Checking Python module updates..."\n' ' DEP_OUT=$("$MM_PY" -m pip install --disable-pip-version-check --upgrade -r "$REPO/scripts/requirements.txt" 2>&1)\n' ' DEP_EXIT=$?\n'