huy
This commit is contained in:
482
MPV/LUA/main.lua
482
MPV/LUA/main.lua
@@ -42,6 +42,7 @@ local LOAD_URL_MENU_TYPE = 'medios_load_url'
|
||||
|
||||
local DOWNLOAD_FORMAT_MENU_TYPE = 'medios_download_pick_format'
|
||||
local DOWNLOAD_STORE_MENU_TYPE = 'medios_download_pick_store'
|
||||
local SCREENSHOT_TAG_MENU_TYPE = 'medeia_screenshot_tags'
|
||||
|
||||
-- Menu types for the command submenu and trim prompt
|
||||
local CMD_MENU_TYPE = 'medios_cmd_menu'
|
||||
@@ -386,6 +387,12 @@ local function _path_exists(path)
|
||||
return utils.file_info(path) ~= nil
|
||||
end
|
||||
|
||||
local function _normalize_fs_path(path)
|
||||
path = trim(tostring(path or ''))
|
||||
path = path:gsub('^"+', ''):gsub('"+$', '')
|
||||
return trim(path)
|
||||
end
|
||||
|
||||
local function _build_python_candidates(configured_python, prefer_no_console)
|
||||
local candidates = {}
|
||||
local seen = {}
|
||||
@@ -585,6 +592,9 @@ end
|
||||
|
||||
-- Forward declaration (defined later) used by helper auto-start.
|
||||
local _resolve_python_exe
|
||||
local _refresh_store_cache
|
||||
local _uosc_open_list_picker
|
||||
local _run_pipeline_detached
|
||||
|
||||
local _cached_store_names = {}
|
||||
local _store_cache_loaded = false
|
||||
@@ -830,18 +840,15 @@ local function attempt_start_pipeline_helper_async(callback)
|
||||
return
|
||||
end
|
||||
|
||||
local cwd = _detect_repo_root()
|
||||
local cwd_arg = cwd ~= '' and cwd or nil
|
||||
|
||||
local args = { python, '-m', 'MPV.pipeline_helper', '--ipc', get_mpv_ipc_path(), '--timeout', '30' }
|
||||
_lua_log('attempt_start_pipeline_helper_async: spawning helper python=' .. tostring(python) .. ' cwd=' .. tostring(cwd_arg or ''))
|
||||
_lua_log('attempt_start_pipeline_helper_async: spawning helper python=' .. tostring(python))
|
||||
|
||||
-- Spawn detached; don't wait for it here (async).
|
||||
local ok, result, detail = _run_subprocess_command({ name = 'subprocess', args = args, cwd = cwd_arg, detach = true })
|
||||
local ok, result, detail = _run_subprocess_command({ name = 'subprocess', args = args, detach = true })
|
||||
_lua_log('attempt_start_pipeline_helper_async: detached spawn result ' .. tostring(detail or ''))
|
||||
if not ok then
|
||||
_lua_log('attempt_start_pipeline_helper_async: detached spawn failed, retrying blocking')
|
||||
ok, result, detail = _run_subprocess_command({ name = 'subprocess', args = args, cwd = cwd_arg })
|
||||
ok, result, detail = _run_subprocess_command({ name = 'subprocess', args = args })
|
||||
_lua_log('attempt_start_pipeline_helper_async: blocking spawn result ' .. tostring(detail or ''))
|
||||
end
|
||||
|
||||
@@ -913,6 +920,7 @@ local function _run_helper_request_async(req, timeout_seconds, cb)
|
||||
|
||||
local function done(resp, err)
|
||||
local err_text = err and tostring(err) or ''
|
||||
local quiet = type(req) == 'table' and req.quiet and true or false
|
||||
local is_timeout = err_text:find('timeout waiting response', 1, true) ~= nil
|
||||
local retry_count = type(req) == 'table' and tonumber(req._retry or 0) or 0
|
||||
local is_retryable = is_timeout and type(req) == 'table'
|
||||
@@ -938,7 +946,11 @@ local function _run_helper_request_async(req, timeout_seconds, cb)
|
||||
end
|
||||
|
||||
if err then
|
||||
_lua_log('ipc-async: done id=' .. tostring(id) .. ' ERROR: ' .. tostring(err))
|
||||
if quiet then
|
||||
_lua_log('ipc-async: done id=' .. tostring(id) .. ' unavailable ' .. tostring(label))
|
||||
else
|
||||
_lua_log('ipc-async: done id=' .. tostring(id) .. ' ERROR: ' .. tostring(err))
|
||||
end
|
||||
else
|
||||
_lua_log('ipc-async: done id=' .. tostring(id) .. ' success=' .. tostring(resp and resp.success))
|
||||
end
|
||||
@@ -1044,88 +1056,16 @@ local function _run_helper_request_async(req, timeout_seconds, cb)
|
||||
ensure_helper_and_send()
|
||||
end
|
||||
|
||||
local function _run_helper_request_response(req, timeout_seconds)
|
||||
-- Legacy synchronous wrapper for compatibility with run_pipeline_via_ipc_response.
|
||||
-- TODO: Migrate all callers to async _run_helper_request_async and remove this.
|
||||
_last_ipc_error = ''
|
||||
if not ensure_pipeline_helper_running() then
|
||||
local rv = tostring(mp.get_property(PIPELINE_READY_PROP) or mp.get_property_native(PIPELINE_READY_PROP) or '')
|
||||
_lua_log('ipc: helper not ready (ready=' .. rv .. '); attempting request anyway')
|
||||
_last_ipc_error = 'helper not ready'
|
||||
end
|
||||
|
||||
do
|
||||
-- Best-effort wait for heartbeat, but do not hard-fail the request.
|
||||
local deadline = mp.get_time() + 1.5
|
||||
while mp.get_time() < deadline do
|
||||
if _is_pipeline_helper_ready() then
|
||||
break
|
||||
end
|
||||
mp.wait_event(0.05)
|
||||
end
|
||||
if not _is_pipeline_helper_ready() then
|
||||
local rv = tostring(mp.get_property(PIPELINE_READY_PROP) or mp.get_property_native(PIPELINE_READY_PROP) or '')
|
||||
_lua_log('ipc: proceeding without helper heartbeat; ready=' .. rv)
|
||||
_last_ipc_error = 'helper heartbeat missing (ready=' .. rv .. ')'
|
||||
end
|
||||
end
|
||||
|
||||
if type(req) ~= 'table' then
|
||||
return nil
|
||||
end
|
||||
|
||||
local id = tostring(req.id or '')
|
||||
if id == '' then
|
||||
id = tostring(math.floor(mp.get_time() * 1000)) .. '-' .. tostring(math.random(100000, 999999))
|
||||
req.id = id
|
||||
end
|
||||
|
||||
local label = ''
|
||||
if req.op then
|
||||
label = 'op=' .. tostring(req.op)
|
||||
elseif req.pipeline then
|
||||
label = 'cmd=' .. tostring(req.pipeline)
|
||||
else
|
||||
label = '(unknown)'
|
||||
end
|
||||
_lua_log('ipc: send request id=' .. tostring(id) .. ' ' .. label)
|
||||
|
||||
local req_json = utils.format_json(req)
|
||||
_last_ipc_last_req_json = req_json
|
||||
mp.set_property(PIPELINE_RESP_PROP, '')
|
||||
mp.set_property(PIPELINE_REQ_PROP, req_json)
|
||||
-- Read-back for debugging: confirms MPV accepted the property write.
|
||||
local echoed = mp.get_property(PIPELINE_REQ_PROP) or ''
|
||||
if echoed == '' then
|
||||
_lua_log('ipc: WARNING request property echoed empty after set')
|
||||
end
|
||||
|
||||
local deadline = mp.get_time() + (timeout_seconds or 5)
|
||||
while mp.get_time() < deadline do
|
||||
local resp_json = mp.get_property(PIPELINE_RESP_PROP)
|
||||
if resp_json and resp_json ~= '' then
|
||||
_last_ipc_last_resp_json = resp_json
|
||||
local ok, resp = pcall(utils.parse_json, resp_json)
|
||||
if ok and resp and resp.id == id then
|
||||
_lua_log('ipc: got response id=' .. tostring(id) .. ' success=' .. tostring(resp.success))
|
||||
return resp
|
||||
end
|
||||
end
|
||||
mp.wait_event(0.05)
|
||||
end
|
||||
|
||||
_lua_log('ipc: timeout waiting response; ' .. label)
|
||||
_last_ipc_error = 'timeout waiting response (' .. label .. ')'
|
||||
return nil
|
||||
end
|
||||
|
||||
-- IPC helper: return the whole response object (stdout/stderr/error/table)
|
||||
local function run_pipeline_via_ipc_response(pipeline_cmd, seeds, timeout_seconds)
|
||||
local function run_pipeline_via_ipc_async(pipeline_cmd, seeds, timeout_seconds, cb)
|
||||
local req = { pipeline = pipeline_cmd }
|
||||
if seeds then
|
||||
req.seeds = seeds
|
||||
end
|
||||
return _run_helper_request_response(req, timeout_seconds)
|
||||
_run_helper_request_async(req, timeout_seconds, function(resp, err)
|
||||
if type(cb) == 'function' then
|
||||
cb(resp, err)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local function _url_can_direct_load(url)
|
||||
@@ -1330,28 +1270,16 @@ local function _check_store_for_existing_url(store, url, cb)
|
||||
local needle = tostring(needles[idx])
|
||||
idx = idx + 1
|
||||
local query = 'url:' .. needle
|
||||
local pipeline_cmd = 'search-file -query ' .. quote_pipeline_arg(query) .. ' | output-json'
|
||||
|
||||
_lua_log('store-check: probing global query=' .. tostring(query))
|
||||
_run_helper_request_async({ pipeline = pipeline_cmd }, 4.0, function(resp, err)
|
||||
_run_helper_request_async({ op = 'url-exists', data = { url = url, needles = { needle } }, quiet = true }, 2.5, function(resp, err)
|
||||
if resp and resp.success then
|
||||
local output = trim(tostring(resp.stdout or ''))
|
||||
if output == '' then
|
||||
local data = resp.data
|
||||
if type(data) ~= 'table' or #data == 0 then
|
||||
run_next(nil)
|
||||
return
|
||||
end
|
||||
|
||||
local ok, data = pcall(utils.parse_json, output)
|
||||
if ok and type(data) == 'table' then
|
||||
if #data > 0 then
|
||||
cb(data, nil, needle)
|
||||
return
|
||||
end
|
||||
run_next(nil)
|
||||
return
|
||||
end
|
||||
|
||||
run_next('malformed JSON response')
|
||||
cb(data, nil, needle)
|
||||
return
|
||||
end
|
||||
|
||||
@@ -1556,6 +1484,160 @@ local function _strip_title_extension(title, path)
|
||||
return title
|
||||
end
|
||||
|
||||
local _pending_screenshot = nil
|
||||
|
||||
local function _normalize_tag_list(value)
|
||||
local tags = {}
|
||||
local seen = {}
|
||||
|
||||
local function add_tag(text)
|
||||
text = trim(tostring(text or ''))
|
||||
if text == '' then
|
||||
return
|
||||
end
|
||||
local key = text:lower()
|
||||
if seen[key] then
|
||||
return
|
||||
end
|
||||
seen[key] = true
|
||||
tags[#tags + 1] = text
|
||||
end
|
||||
|
||||
if type(value) == 'table' then
|
||||
for _, item in ipairs(value) do
|
||||
add_tag(item)
|
||||
end
|
||||
return tags
|
||||
end
|
||||
|
||||
local text = tostring(value or '')
|
||||
for token in text:gmatch('[^,;\r\n]+') do
|
||||
add_tag(token)
|
||||
end
|
||||
return tags
|
||||
end
|
||||
|
||||
local function _start_screenshot_store_save(store, out_path, tags)
|
||||
store = _normalize_store_name(store)
|
||||
out_path = _normalize_fs_path(out_path)
|
||||
if store == '' or out_path == '' then
|
||||
mp.osd_message('Screenshot upload failed: invalid store or path', 5)
|
||||
return false
|
||||
end
|
||||
|
||||
local tag_list = _normalize_tag_list(tags)
|
||||
local cmd = 'add-file -store ' .. quote_pipeline_arg(store)
|
||||
local seeds = { { path = out_path } }
|
||||
if #tag_list > 0 then
|
||||
seeds[1].tag = tag_list
|
||||
end
|
||||
_set_selected_store(store)
|
||||
|
||||
if _run_pipeline_detached(cmd, function(_, err)
|
||||
mp.osd_message('Screenshot upload failed to start: ' .. tostring(err or 'unknown'), 5)
|
||||
end, seeds) then
|
||||
local tag_suffix = (#tag_list > 0) and (' | tags: ' .. tostring(#tag_list)) or ''
|
||||
mp.osd_message('Screenshot saved to store: ' .. store .. tag_suffix, 3)
|
||||
return true
|
||||
end
|
||||
|
||||
mp.osd_message('Screenshot upload failed to start', 5)
|
||||
return false
|
||||
end
|
||||
|
||||
local function _commit_pending_screenshot(tags)
|
||||
if type(_pending_screenshot) ~= 'table' or not _pending_screenshot.path or not _pending_screenshot.store then
|
||||
return
|
||||
end
|
||||
local store = tostring(_pending_screenshot.store or '')
|
||||
local out_path = tostring(_pending_screenshot.path or '')
|
||||
_pending_screenshot = nil
|
||||
_start_screenshot_store_save(store, out_path, tags)
|
||||
end
|
||||
|
||||
local function _apply_screenshot_tag_query(query)
|
||||
_commit_pending_screenshot(_normalize_tag_list(query))
|
||||
end
|
||||
|
||||
local function _open_screenshot_tag_prompt(store, out_path)
|
||||
store = _normalize_store_name(store)
|
||||
out_path = _normalize_fs_path(out_path)
|
||||
if store == '' or out_path == '' then
|
||||
return
|
||||
end
|
||||
|
||||
_pending_screenshot = { store = store, path = out_path }
|
||||
|
||||
if not ensure_uosc_loaded() then
|
||||
_commit_pending_screenshot(nil)
|
||||
return
|
||||
end
|
||||
|
||||
local menu_data = {
|
||||
type = SCREENSHOT_TAG_MENU_TYPE,
|
||||
title = 'Screenshot tags',
|
||||
search_style = 'palette',
|
||||
search_debounce = 'submit',
|
||||
on_search = { 'script-message-to', mp.get_script_name(), 'medeia-image-screenshot-tags-search' },
|
||||
footnote = 'Optional comma-separated tags. Press Enter to save, or choose Save without tags.',
|
||||
items = {
|
||||
{
|
||||
title = 'Save without tags',
|
||||
hint = 'Skip optional tags',
|
||||
value = { 'script-message-to', mp.get_script_name(), 'medeia-image-screenshot-tags-event', utils.format_json({ query = '' }) },
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
mp.commandv('script-message-to', 'uosc', 'open-menu', utils.format_json(menu_data))
|
||||
end
|
||||
|
||||
local function _open_store_picker_for_pending_screenshot()
|
||||
if type(_pending_screenshot) ~= 'table' or not _pending_screenshot.path then
|
||||
return
|
||||
end
|
||||
|
||||
local function build_items()
|
||||
local selected = _get_selected_store()
|
||||
local items = {}
|
||||
|
||||
if type(_cached_store_names) == 'table' and #_cached_store_names > 0 then
|
||||
for _, name in ipairs(_cached_store_names) do
|
||||
name = trim(tostring(name or ''))
|
||||
if name ~= '' then
|
||||
items[#items + 1] = {
|
||||
title = name,
|
||||
hint = (selected ~= '' and name == selected) and 'Current store' or '',
|
||||
active = (selected ~= '' and name == selected) and true or false,
|
||||
value = { 'script-message-to', mp.get_script_name(), 'medeia-image-screenshot-pick-store', utils.format_json({ store = name }) },
|
||||
}
|
||||
end
|
||||
end
|
||||
else
|
||||
items[#items + 1] = {
|
||||
title = 'No stores found',
|
||||
hint = 'Configure stores in config.conf',
|
||||
selectable = false,
|
||||
}
|
||||
end
|
||||
|
||||
return items
|
||||
end
|
||||
|
||||
_uosc_open_list_picker(DOWNLOAD_STORE_MENU_TYPE, 'Save screenshot', build_items())
|
||||
|
||||
mp.add_timeout(0.05, function()
|
||||
if type(_pending_screenshot) ~= 'table' or not _pending_screenshot.path then
|
||||
return
|
||||
end
|
||||
_refresh_store_cache(1.5, function(success, changed)
|
||||
if success and changed then
|
||||
_uosc_open_list_picker(DOWNLOAD_STORE_MENU_TYPE, 'Save screenshot', build_items())
|
||||
end
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
local function _capture_screenshot()
|
||||
local function _format_time_label(seconds)
|
||||
local total = math.max(0, math.floor(tonumber(seconds or 0) or 0))
|
||||
@@ -1585,8 +1667,12 @@ local function _capture_screenshot()
|
||||
local safe_title = _sanitize_filename_component(raw_title)
|
||||
|
||||
local filename = safe_title .. '_' .. label .. '.png'
|
||||
local temp_dir = mp.get_property('user-data/medeia-config-temp') or os.getenv('TEMP') or os.getenv('TMP') or '/tmp'
|
||||
local temp_dir = _normalize_fs_path(mp.get_property('user-data/medeia-config-temp'))
|
||||
if temp_dir == '' then
|
||||
temp_dir = _normalize_fs_path(os.getenv('TEMP') or os.getenv('TMP') or '/tmp')
|
||||
end
|
||||
local out_path = utils.join_path(temp_dir, filename)
|
||||
out_path = _normalize_fs_path(out_path)
|
||||
|
||||
local function do_screenshot(mode)
|
||||
mode = mode or 'video'
|
||||
@@ -1611,28 +1697,35 @@ local function _capture_screenshot()
|
||||
end
|
||||
|
||||
_ensure_selected_store_loaded()
|
||||
local selected_store = _get_selected_store()
|
||||
selected_store = trim(tostring(selected_store or ''))
|
||||
selected_store = selected_store:gsub('^\"', ''):gsub('\"$', '')
|
||||
|
||||
if selected_store == '' then
|
||||
mp.osd_message('Select a store first (Store button)', 2)
|
||||
return
|
||||
local function dispatch_screenshot_save()
|
||||
local store_count = (type(_cached_store_names) == 'table') and #_cached_store_names or 0
|
||||
local selected_store = _normalize_store_name(_get_selected_store())
|
||||
|
||||
if store_count > 1 then
|
||||
_pending_screenshot = { path = out_path }
|
||||
_open_store_picker_for_pending_screenshot()
|
||||
return
|
||||
end
|
||||
|
||||
if selected_store == '' and store_count == 1 then
|
||||
selected_store = _normalize_store_name(_cached_store_names[1])
|
||||
end
|
||||
|
||||
if selected_store == '' then
|
||||
mp.osd_message('Select a store first (Store button)', 2)
|
||||
return
|
||||
end
|
||||
|
||||
_open_screenshot_tag_prompt(selected_store, out_path)
|
||||
end
|
||||
|
||||
mp.osd_message('Saving screenshot...', 1)
|
||||
|
||||
-- optimization: use persistent pipeline helper instead of spawning new python process
|
||||
-- escape paths for command line
|
||||
local cmd = 'add-file -store "' .. selected_store .. '" -path "' .. out_path .. '"'
|
||||
|
||||
local resp = run_pipeline_via_ipc_response(cmd, nil, 10)
|
||||
|
||||
if resp and resp.success then
|
||||
mp.osd_message('Screenshot saved to store: ' .. selected_store, 3)
|
||||
|
||||
if not _store_cache_loaded then
|
||||
_refresh_store_cache(1.5, function()
|
||||
dispatch_screenshot_save()
|
||||
end)
|
||||
else
|
||||
local err = (resp and resp.error) or (resp and resp.stderr) or 'IPC error'
|
||||
mp.osd_message('Screenshot upload failed: ' .. tostring(err), 5)
|
||||
dispatch_screenshot_save()
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1640,6 +1733,36 @@ mp.register_script_message('medeia-image-screenshot', function()
|
||||
_capture_screenshot()
|
||||
end)
|
||||
|
||||
mp.register_script_message('medeia-image-screenshot-pick-store', function(json)
|
||||
if type(_pending_screenshot) ~= 'table' or not _pending_screenshot.path then
|
||||
return
|
||||
end
|
||||
local ok, ev = pcall(utils.parse_json, json)
|
||||
if not ok or type(ev) ~= 'table' then
|
||||
return
|
||||
end
|
||||
|
||||
local store = _normalize_store_name(ev.store)
|
||||
if store == '' then
|
||||
return
|
||||
end
|
||||
|
||||
local out_path = tostring(_pending_screenshot.path or '')
|
||||
_open_screenshot_tag_prompt(store, out_path)
|
||||
end)
|
||||
|
||||
mp.register_script_message('medeia-image-screenshot-tags-search', function(query)
|
||||
_apply_screenshot_tag_query(query)
|
||||
end)
|
||||
|
||||
mp.register_script_message('medeia-image-screenshot-tags-event', function(json)
|
||||
local ok, ev = pcall(utils.parse_json, json)
|
||||
if not ok or type(ev) ~= 'table' then
|
||||
return
|
||||
end
|
||||
_apply_screenshot_tag_query(ev.query)
|
||||
end)
|
||||
|
||||
|
||||
local CLIP_MARKER_SLOT_COUNT = 2
|
||||
local clip_markers = {}
|
||||
@@ -2061,7 +2184,7 @@ local function _run_pipeline_request_async(pipeline_cmd, seeds, timeout_seconds,
|
||||
_run_helper_request_async(req, timeout_seconds or 30, cb)
|
||||
end
|
||||
|
||||
local function _refresh_store_cache(timeout_seconds, on_complete)
|
||||
_refresh_store_cache = function(timeout_seconds, on_complete)
|
||||
ensure_mpv_ipc_server()
|
||||
|
||||
local prev_count = (type(_cached_store_names) == 'table') and #_cached_store_names or 0
|
||||
@@ -2174,7 +2297,7 @@ local function _refresh_store_cache(timeout_seconds, on_complete)
|
||||
return false
|
||||
end
|
||||
|
||||
local function _uosc_open_list_picker(menu_type, title, items)
|
||||
_uosc_open_list_picker = function(menu_type, title, items)
|
||||
local menu_data = {
|
||||
type = menu_type,
|
||||
title = title,
|
||||
@@ -2606,7 +2729,7 @@ _refresh_current_store_url_status = function(reason)
|
||||
local err_text = trim(tostring(err or ''))
|
||||
if err_text ~= '' then
|
||||
_set_current_store_url_status(store, url, 'error', err_text, 0, needle)
|
||||
_lua_log('store-check: failed err=' .. tostring(err_text))
|
||||
_lua_log('store-check: lookup unavailable')
|
||||
return
|
||||
end
|
||||
|
||||
@@ -3281,10 +3404,6 @@ local function _run_pipeline_cli_detached(pipeline_cmd, seeds)
|
||||
args = args,
|
||||
detach = true,
|
||||
}
|
||||
local repo_root = _detect_repo_root()
|
||||
if repo_root ~= '' then
|
||||
cmd.cwd = repo_root
|
||||
end
|
||||
|
||||
local ok, result, detail = _run_subprocess_command(cmd)
|
||||
if ok then
|
||||
@@ -3296,11 +3415,11 @@ local function _run_pipeline_cli_detached(pipeline_cmd, seeds)
|
||||
return false, detail or _describe_subprocess_result(result)
|
||||
end
|
||||
|
||||
local function _run_pipeline_detached(pipeline_cmd, on_failure)
|
||||
_run_pipeline_detached = function(pipeline_cmd, on_failure, seeds)
|
||||
if not pipeline_cmd or pipeline_cmd == '' then
|
||||
return false
|
||||
end
|
||||
local ok, detail = _run_pipeline_cli_detached(pipeline_cmd, nil)
|
||||
local ok, detail = _run_pipeline_cli_detached(pipeline_cmd, seeds)
|
||||
if ok then
|
||||
return true
|
||||
end
|
||||
@@ -3313,7 +3432,7 @@ local function _run_pipeline_detached(pipeline_cmd, on_failure)
|
||||
return false
|
||||
end
|
||||
|
||||
_run_helper_request_async({ op = 'run-detached', data = { pipeline = pipeline_cmd } }, 1.0, function(resp, err)
|
||||
_run_helper_request_async({ op = 'run-detached', data = { pipeline = pipeline_cmd, seeds = seeds } }, 1.0, function(resp, err)
|
||||
if resp and resp.success then
|
||||
return
|
||||
end
|
||||
@@ -3826,21 +3945,49 @@ end
|
||||
-- Helper to run pipeline and parse JSON output
|
||||
function M.run_pipeline_json(pipeline_cmd, seeds, cb)
|
||||
cb = cb or function() end
|
||||
if not pipeline_cmd:match('output%-json$') then
|
||||
pipeline_cmd = pipeline_cmd .. ' | output-json'
|
||||
pipeline_cmd = trim(tostring(pipeline_cmd or ''))
|
||||
if pipeline_cmd == '' then
|
||||
cb(nil, 'empty pipeline command')
|
||||
return
|
||||
end
|
||||
M.run_pipeline(pipeline_cmd, seeds, function(output, err)
|
||||
if output then
|
||||
local ok, data = pcall(utils.parse_json, output)
|
||||
if ok then
|
||||
cb(data, nil)
|
||||
|
||||
ensure_mpv_ipc_server()
|
||||
|
||||
local lower_cmd = pipeline_cmd:lower()
|
||||
local is_mpv_load = lower_cmd:match('%.mpv%s+%-url') ~= nil
|
||||
local timeout_seconds = is_mpv_load and 45 or 30
|
||||
_run_pipeline_request_async(pipeline_cmd, seeds, timeout_seconds, function(resp, err)
|
||||
if resp and resp.success then
|
||||
if type(resp.data) == 'table' then
|
||||
cb(resp.data, nil)
|
||||
return
|
||||
end
|
||||
_lua_log('Failed to parse JSON: ' .. output)
|
||||
cb(nil, 'malformed JSON response')
|
||||
|
||||
local output = trim(tostring(resp.stdout or ''))
|
||||
if output ~= '' then
|
||||
local ok, data = pcall(utils.parse_json, output)
|
||||
if ok then
|
||||
cb(data, nil)
|
||||
return
|
||||
end
|
||||
_lua_log('Failed to parse JSON: ' .. output)
|
||||
cb(nil, 'malformed JSON response')
|
||||
return
|
||||
end
|
||||
|
||||
cb({}, nil)
|
||||
return
|
||||
end
|
||||
cb(nil, err)
|
||||
|
||||
local details = err or ''
|
||||
if details == '' and type(resp) == 'table' then
|
||||
if resp.error and tostring(resp.error) ~= '' then
|
||||
details = tostring(resp.error)
|
||||
elseif resp.stderr and tostring(resp.stderr) ~= '' then
|
||||
details = tostring(resp.stderr)
|
||||
end
|
||||
end
|
||||
cb(nil, details ~= '' and details or 'unknown')
|
||||
end)
|
||||
end
|
||||
|
||||
@@ -4081,34 +4228,33 @@ local function _start_trim_with_range(range)
|
||||
_lua_log('trim: final upload_cmd=' .. pipeline_cmd)
|
||||
_lua_log('trim: === CALLING PIPELINE HELPER FOR UPLOAD ===')
|
||||
|
||||
-- Optimization: use persistent pipeline helper
|
||||
local response = run_pipeline_via_ipc_response(pipeline_cmd, nil, 60)
|
||||
|
||||
if not response then
|
||||
response = { success = false, error = "Timeout or IPC error" }
|
||||
end
|
||||
|
||||
_lua_log('trim: api response success=' .. tostring(response.success))
|
||||
_lua_log('trim: api response error=' .. tostring(response.error or 'nil'))
|
||||
_lua_log('trim: api response stderr=' .. tostring(response.stderr or 'nil'))
|
||||
_lua_log('trim: api response returncode=' .. tostring(response.returncode or 'nil'))
|
||||
|
||||
if response.stderr and response.stderr ~= '' then
|
||||
_lua_log('trim: STDERR OUTPUT: ' .. response.stderr)
|
||||
end
|
||||
|
||||
if response.success then
|
||||
local msg = 'Trim and upload completed'
|
||||
if selected_store then
|
||||
msg = msg .. ' (store: ' .. selected_store .. ')'
|
||||
run_pipeline_via_ipc_async(pipeline_cmd, nil, 60, function(response, err)
|
||||
if not response then
|
||||
response = { success = false, error = err or 'Timeout or IPC error' }
|
||||
end
|
||||
mp.osd_message(msg, 5)
|
||||
_lua_log('trim: SUCCESS - ' .. msg)
|
||||
else
|
||||
local err_msg = response.error or response.stderr or 'unknown error'
|
||||
mp.osd_message('Upload failed: ' .. err_msg, 5)
|
||||
_lua_log('trim: upload FAILED - ' .. err_msg)
|
||||
end
|
||||
|
||||
_lua_log('trim: api response success=' .. tostring(response.success))
|
||||
_lua_log('trim: api response error=' .. tostring(response.error or 'nil'))
|
||||
_lua_log('trim: api response stderr=' .. tostring(response.stderr or 'nil'))
|
||||
_lua_log('trim: api response returncode=' .. tostring(response.returncode or 'nil'))
|
||||
|
||||
if response.stderr and response.stderr ~= '' then
|
||||
_lua_log('trim: STDERR OUTPUT: ' .. response.stderr)
|
||||
end
|
||||
|
||||
if response.success then
|
||||
local msg = 'Trim and upload completed'
|
||||
if selected_store then
|
||||
msg = msg .. ' (store: ' .. selected_store .. ')'
|
||||
end
|
||||
mp.osd_message(msg, 5)
|
||||
_lua_log('trim: SUCCESS - ' .. msg)
|
||||
else
|
||||
local err_msg = err or response.error or response.stderr or 'unknown error'
|
||||
mp.osd_message('Upload failed: ' .. err_msg, 5)
|
||||
_lua_log('trim: upload FAILED - ' .. err_msg)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function M.open_trim_prompt()
|
||||
|
||||
Reference in New Issue
Block a user