From 4dd7556e858aabba52c85e01c3fc0e007972221f Mon Sep 17 00:00:00 2001 From: Nose Date: Sun, 15 Mar 2026 01:51:10 -0700 Subject: [PATCH] fix streaming with lyric notes in mpv --- API/data/alldebrid.json | 6 +- MPV/LUA/main.lua | 282 ++++++++++++++++++++++++++++++++-------- MPV/lyric.py | 2 + 3 files changed, 233 insertions(+), 57 deletions(-) diff --git a/API/data/alldebrid.json b/API/data/alldebrid.json index 3c92ea5..52119c2 100644 --- a/API/data/alldebrid.json +++ b/API/data/alldebrid.json @@ -92,7 +92,7 @@ "(hitfile\\.net/[a-z0-9A-Z]{4,9})" ], "regexp": "(hitf\\.(to|cc)/([a-z0-9A-Z]{4,9}))|(htfl\\.(net|to|cc)/([a-z0-9A-Z]{4,9}))|(hitfile\\.(net)/download/free/([a-z0-9A-Z]{4,9}))|((hitfile\\.net/[a-z0-9A-Z]{4,9}))", - "status": true + "status": false }, "mega": { "name": "mega", @@ -618,7 +618,7 @@ "(upload42\\.com/[0-9a-zA-Z]{12})" ], "regexp": "(upload42\\.com/[0-9a-zA-Z]{12})", - "status": true + "status": false }, "uploadbank": { "name": "uploadbank", @@ -690,7 +690,7 @@ "uploadrar\\.(net|com)/([0-9a-z]{12})" ], "regexp": "((get|cloud)\\.rahim-soft\\.com/([0-9a-z]{12}))|((fingau\\.com/([0-9a-z]{12})))|((tech|miui|cloud|flash)\\.getpczone\\.com/([0-9a-z]{12}))|(miui.rahim-soft\\.com/([0-9a-z]{12}))|(uploadrar\\.(net|com)/([0-9a-z]{12}))", - "status": false, + "status": true, "hardRedirect": [ "uploadrar.com/([0-9a-zA-Z]{12})" ] diff --git a/MPV/LUA/main.lua b/MPV/LUA/main.lua index 268ee09..6e2d827 100644 --- a/MPV/LUA/main.lua +++ b/MPV/LUA/main.lua @@ -46,6 +46,8 @@ local DOWNLOAD_STORE_MENU_TYPE = 'medios_download_pick_store' -- Menu types for the command submenu and trim prompt local CMD_MENU_TYPE = 'medios_cmd_menu' local TRIM_PROMPT_MENU_TYPE = 'medios_trim_prompt' +local SLEEP_PROMPT_MENU_TYPE = 'medeia_sleep_timer_prompt' +local _sleep_timer = nil local PIPELINE_REQ_PROP = 'user-data/medeia-pipeline-request' local PIPELINE_RESP_PROP = 'user-data/medeia-pipeline-response' @@ -277,6 +279,66 @@ local function find_file_upwards(start_dir, relative_path, max_levels) return nil end +local function _append_unique_path(out, seen, path) + path = trim(tostring(path or '')) + if path == '' then + return + end + local key = path:gsub('\\', '/'):lower() + if seen[key] then + return + end + seen[key] = true + out[#out + 1] = path +end + +local function _build_sibling_script_candidates(file_name) + local candidates = {} + local seen = {} + local script_dir = mp.get_script_directory() or '' + local cwd = utils.getcwd() or '' + + if script_dir ~= '' then + _append_unique_path(candidates, seen, script_dir .. '/' .. file_name) + _append_unique_path(candidates, seen, script_dir .. '/LUA/' .. file_name) + _append_unique_path(candidates, seen, script_dir .. '/../' .. file_name) + _append_unique_path(candidates, seen, find_file_upwards(script_dir, 'MPV/LUA/' .. file_name, 8)) + end + + if cwd ~= '' then + _append_unique_path(candidates, seen, find_file_upwards(cwd, 'MPV/LUA/' .. file_name, 8)) + end + + return candidates +end + +local function _load_lua_chunk_from_candidates(label, file_name) + local candidates = _build_sibling_script_candidates(file_name) + local last_error = nil + + for _, candidate in ipairs(candidates) do + local ok_load, chunk_or_err, load_err = pcall(loadfile, candidate) + if ok_load and chunk_or_err then + local ok_run, result_or_err = pcall(chunk_or_err) + if ok_run then + _lua_log(label .. ': loaded from ' .. candidate) + return true, result_or_err, candidate + end + last_error = tostring(result_or_err or 'runtime error') + _lua_log(label .. ': runtime error at ' .. candidate .. ' (' .. last_error .. ')') + elseif ok_load then + last_error = tostring(load_err or 'loadfile failed') + _lua_log(label .. ': load failed at ' .. candidate .. ' (' .. last_error .. ')') + else + last_error = tostring(chunk_or_err or 'loadfile failed') + _lua_log(label .. ': load failed at ' .. candidate .. ' (' .. last_error .. ')') + end + end + + _lua_log(label .. ': load failed; candidates=' .. tostring(#candidates) .. ' last_error=' .. tostring(last_error or 'not found')) + return false, nil, nil, last_error +end + -- Forward declaration (defined later) used by helper auto-start. local _resolve_python_exe @@ -1309,6 +1371,153 @@ mp.register_script_message('medeia-audio-only', function() _audio_only() end) +local function _cancel_sleep_timer(show_message) + if _sleep_timer ~= nil then + pcall(function() + _sleep_timer:kill() + end) + _sleep_timer = nil + end + if show_message then + mp.osd_message('Sleep timer cancelled', 1.5) + end +end + +local function _parse_sleep_minutes(text) + local s = trim(tostring(text or '')):lower() + if s == '' then + return nil + end + + if s == 'off' or s == 'cancel' or s == 'stop' or s == '0' then + return 0 + end + + local hours = s:match('^([%d%.]+)%s*h$') + if hours then + local value = tonumber(hours) + if value and value > 0 then + return value * 60 + end + return nil + end + + local mins = s:match('^([%d%.]+)%s*m$') + if mins then + local value = tonumber(mins) + if value and value >= 0 then + return value + end + return nil + end + + local value = tonumber(s) + if value and value >= 0 then + return value + end + + return nil +end + +local function _open_sleep_timer_prompt() + local items = { + { + title = '15 minutes', + hint = 'Quick preset', + value = { 'script-message-to', mp.get_script_name(), 'medeia-sleep-timer-event', utils.format_json({ type = 'search', query = '15' }) }, + }, + { + title = '30 minutes', + hint = 'Quick preset', + value = { 'script-message-to', mp.get_script_name(), 'medeia-sleep-timer-event', utils.format_json({ type = 'search', query = '30' }) }, + }, + { + title = '60 minutes', + hint = 'Quick preset', + value = { 'script-message-to', mp.get_script_name(), 'medeia-sleep-timer-event', utils.format_json({ type = 'search', query = '60' }) }, + }, + { + title = 'Cancel timer', + hint = 'Also accepts off / 0 / cancel', + value = { 'script-message-to', mp.get_script_name(), 'medeia-sleep-timer-event', utils.format_json({ type = 'search', query = '0' }) }, + }, + } + + local menu_data = { + type = SLEEP_PROMPT_MENU_TYPE, + title = 'Sleep Timer', + search_style = 'palette', + search_debounce = 'submit', + on_search = { 'script-message-to', mp.get_script_name(), 'medeia-sleep-timer-search' }, + footnote = 'Enter minutes (30), or use 1h / 1.5h. Enter 0 to cancel.', + items = items, + } + + if ensure_uosc_loaded() then + mp.commandv('script-message-to', 'uosc', 'open-menu', utils.format_json(menu_data)) + else + mp.osd_message('Sleep timer unavailable (uosc not loaded)', 2.0) + end +end + +local function _apply_sleep_timer_query(query) + local minutes = _parse_sleep_minutes(query) + if minutes == nil then + mp.osd_message('Sleep timer: enter minutes, 1h, or 0 to cancel', 2.0) + return + end + + if minutes <= 0 then + _cancel_sleep_timer(true) + pcall(function() + mp.commandv('script-message-to', 'uosc', 'close-menu', SLEEP_PROMPT_MENU_TYPE) + end) + return + end + + _cancel_sleep_timer(false) + + local seconds = math.max(1, math.floor(minutes * 60)) + _sleep_timer = mp.add_timeout(seconds, function() + _sleep_timer = nil + mp.osd_message('Sleep timer: closing mpv', 1.5) + mp.commandv('quit') + end) + + mp.osd_message(string.format('Sleep timer set: %d min', math.floor(minutes + 0.5)), 1.5) + _lua_log('sleep: timer set minutes=' .. tostring(minutes) .. ' seconds=' .. tostring(seconds)) + + pcall(function() + mp.commandv('script-message-to', 'uosc', 'close-menu', SLEEP_PROMPT_MENU_TYPE) + end) +end + +local function _handle_sleep_timer_event(json) + local ok, ev = pcall(utils.parse_json, json) + if not ok or type(ev) ~= 'table' then + _lua_log('sleep: invalid event payload=' .. tostring(json)) + return + end + + if ev.type ~= 'search' then + return + end + + _apply_sleep_timer_query(ev.query) +end + +mp.register_script_message('medeia-sleep-timer', function() + _open_sleep_timer_prompt() +end) + +mp.register_script_message('medeia-sleep-timer-event', function(json) + _handle_sleep_timer_event(json) +end) + +mp.register_script_message('medeia-sleep-timer-search', function(query) + _apply_sleep_timer_query(query) +end) + local function _bind_image_key(key, name, fn, opts) opts = opts or {} if ImageControl.binding_names[name] then @@ -2680,43 +2889,22 @@ local function _start_trim_with_range(range) _lua_log('=== TRIM START: range=' .. tostring(range)) mp.osd_message('Trimming...', 10) - -- Load the trim module for direct FFmpeg trimming - local script_dir = mp.get_script_directory() - _lua_log('trim: script_dir=' .. tostring(script_dir)) - - -- Try multiple locations for trim.lua - local trim_paths = {} - if script_dir and script_dir ~= '' then - table.insert(trim_paths, script_dir .. '/trim.lua') - table.insert(trim_paths, script_dir .. '/LUA/trim.lua') -- if called from parent - table.insert(trim_paths, script_dir .. '/../trim.lua') - end - - -- Also try absolute path - table.insert(trim_paths, '/medios/Medios-Macina/MPV/LUA/trim.lua') - table.insert(trim_paths, 'C:/medios/Medios-Macina/MPV/LUA/trim.lua') - local trim_module = nil + local trim_path = nil local load_err = nil + local ok_trim = false - for _, trim_path in ipairs(trim_paths) do - _lua_log('trim: trying path=' .. trim_path) - local ok, result = pcall(loadfile, trim_path) - if ok and result then - trim_module = result() - _lua_log('trim: loaded successfully from ' .. trim_path) - break - else - load_err = tostring(result or 'unknown error') - _lua_log('trim: failed to load from ' .. trim_path .. ' (' .. load_err .. ')') - end - end - + ok_trim, trim_module, trim_path, load_err = _load_lua_chunk_from_candidates('trim', 'trim.lua') + if not trim_module or not trim_module.trim_file then mp.osd_message('ERROR: Could not load trim module from any path', 3) _lua_log('trim: FAILED - all paths exhausted, last error=' .. tostring(load_err)) return end + + if ok_trim and trim_path then + _lua_log('trim: using module at ' .. tostring(trim_path)) + end range = trim(tostring(range or '')) _lua_log('trim: after_trim range=' .. tostring(range)) @@ -3200,13 +3388,18 @@ mp.add_timeout(0, function() pcall(ensure_mpv_ipc_server) pcall(_lua_log, 'medeia-lua loaded version=' .. MEDEIA_LUA_VERSION) - attempt_start_pipeline_helper_async(function(success) - if success then - _lua_log('helper-auto-start succeeded') - else - _lua_log('helper-auto-start failed') - end + local ok_helper, helper_err = pcall(function() + attempt_start_pipeline_helper_async(function(success) + if success then + _lua_log('helper-auto-start succeeded') + else + _lua_log('helper-auto-start failed') + end + end) end) + if not ok_helper then + _lua_log('helper-auto-start raised: ' .. tostring(helper_err)) + end -- Try to re-register right-click after UOSC loads (might override its binding) mp.add_timeout(1.0, function() @@ -3218,25 +3411,6 @@ mp.add_timeout(0, function() end, {repeatable=false}) end) end) - - -- Load optional modules (kept in separate files). - pcall(function() - local script_dir = mp.get_script_directory() or '' - local candidates = {} - if script_dir ~= '' then - table.insert(candidates, script_dir .. '/sleep_timer.lua') - table.insert(candidates, script_dir .. '/LUA/sleep_timer.lua') - table.insert(candidates, script_dir .. '/../sleep_timer.lua') - end - table.insert(candidates, 'C:/medios/Medios-Macina/MPV/LUA/sleep_timer.lua') - for _, p in ipairs(candidates) do - local ok, chunk = pcall(loadfile, p) - if ok and chunk then - pcall(chunk) - break - end - end - end) end) return M diff --git a/MPV/lyric.py b/MPV/lyric.py index be2da23..59e9dfd 100644 --- a/MPV/lyric.py +++ b/MPV/lyric.py @@ -1208,6 +1208,8 @@ def run_auto_overlay( autofetch_enabled = bool(cfg.get("lyric_autofetch", True)) now = time.time() if ( + not lrc_text + and autofetch_enabled and state.key != state.fetch_attempt_key and (now - state.fetch_attempt_at) > 2.0