fd
Some checks failed
smoke-mm / Install & smoke test mm --help (push) Has been cancelled
Some checks failed
smoke-mm / Install & smoke test mm --help (push) Has been cancelled
This commit is contained in:
577
MPV/LUA/main.lua
577
MPV/LUA/main.lua
@@ -6,6 +6,9 @@ local M = {}
|
||||
|
||||
local MEDEIA_LUA_VERSION = '2025-12-24'
|
||||
|
||||
-- Expose a tiny breadcrumb for debugging which script version is loaded.
|
||||
pcall(mp.set_property, 'user-data/medeia-lua-version', MEDEIA_LUA_VERSION)
|
||||
|
||||
-- Track whether uosc is available so menu calls don't fail with
|
||||
-- "Can't find script 'uosc' to send message to."
|
||||
local _uosc_loaded = false
|
||||
@@ -57,6 +60,24 @@ local function _lua_log(text)
|
||||
end
|
||||
local dir = ''
|
||||
|
||||
-- Prefer a stable repo-root Log/ folder based on the script directory.
|
||||
do
|
||||
local function _dirname(p)
|
||||
p = tostring(p or '')
|
||||
p = p:gsub('[/\\]+$', '')
|
||||
return p:match('(.*)[/\\]') or ''
|
||||
end
|
||||
|
||||
local base = mp.get_script_directory() or ''
|
||||
if base ~= '' then
|
||||
-- base is expected to be <repo>/MPV/LUA
|
||||
local root = _dirname(_dirname(base))
|
||||
if root ~= '' then
|
||||
dir = utils.join_path(root, 'Log')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Prefer repo-root Log/ for consistency with Python helper logs.
|
||||
do
|
||||
local function find_up(start_dir, relative_path, max_levels)
|
||||
@@ -122,6 +143,8 @@ local function _lua_log(text)
|
||||
end
|
||||
end
|
||||
|
||||
_lua_log('medeia lua loaded version=' .. tostring(MEDEIA_LUA_VERSION) .. ' script=' .. tostring(mp.get_script_name()))
|
||||
|
||||
local function ensure_uosc_loaded()
|
||||
if _uosc_loaded or _is_script_loaded('uosc') then
|
||||
_uosc_loaded = true
|
||||
@@ -274,13 +297,107 @@ end
|
||||
local _cached_store_names = {}
|
||||
local _store_cache_loaded = false
|
||||
|
||||
local SELECTED_STORE_PROP = 'user-data/medeia-selected-store'
|
||||
local STORE_PICKER_MENU_TYPE = 'medeia_store_picker'
|
||||
local _selected_store_loaded = false
|
||||
|
||||
local function _get_script_opts_dir()
|
||||
local dir = nil
|
||||
pcall(function()
|
||||
dir = mp.command_native({ 'expand-path', '~~/script-opts' })
|
||||
end)
|
||||
if type(dir) ~= 'string' or dir == '' then
|
||||
return nil
|
||||
end
|
||||
return dir
|
||||
end
|
||||
|
||||
local function _get_selected_store_conf_path()
|
||||
local dir = _get_script_opts_dir()
|
||||
if not dir then
|
||||
return nil
|
||||
end
|
||||
return utils.join_path(dir, 'medeia.conf')
|
||||
end
|
||||
|
||||
local function _load_selected_store_from_disk()
|
||||
local path = _get_selected_store_conf_path()
|
||||
if not path then
|
||||
return nil
|
||||
end
|
||||
local fh = io.open(path, 'r')
|
||||
if not fh then
|
||||
return nil
|
||||
end
|
||||
for line in fh:lines() do
|
||||
local s = trim(tostring(line or ''))
|
||||
if s ~= '' and s:sub(1, 1) ~= '#' and s:sub(1, 1) ~= ';' then
|
||||
local k, v = s:match('^([%w_%-]+)%s*=%s*(.*)$')
|
||||
if k and v and k:lower() == 'store' then
|
||||
fh:close()
|
||||
v = trim(tostring(v or ''))
|
||||
return v ~= '' and v or nil
|
||||
end
|
||||
end
|
||||
end
|
||||
fh:close()
|
||||
return nil
|
||||
end
|
||||
|
||||
local function _save_selected_store_to_disk(store)
|
||||
local path = _get_selected_store_conf_path()
|
||||
if not path then
|
||||
return false
|
||||
end
|
||||
local fh = io.open(path, 'w')
|
||||
if not fh then
|
||||
return false
|
||||
end
|
||||
fh:write('# Medeia MPV script options\n')
|
||||
fh:write('store=' .. tostring(store or '') .. '\n')
|
||||
fh:close()
|
||||
return true
|
||||
end
|
||||
|
||||
local function _get_selected_store()
|
||||
local v = ''
|
||||
pcall(function()
|
||||
v = tostring(mp.get_property(SELECTED_STORE_PROP) or '')
|
||||
end)
|
||||
return trim(tostring(v or ''))
|
||||
end
|
||||
|
||||
local function _set_selected_store(store)
|
||||
store = trim(tostring(store or ''))
|
||||
pcall(mp.set_property, SELECTED_STORE_PROP, store)
|
||||
pcall(_save_selected_store_to_disk, store)
|
||||
end
|
||||
|
||||
local function _ensure_selected_store_loaded()
|
||||
if _selected_store_loaded then
|
||||
return
|
||||
end
|
||||
_selected_store_loaded = true
|
||||
local disk = nil
|
||||
pcall(function()
|
||||
disk = _load_selected_store_from_disk()
|
||||
end)
|
||||
disk = trim(tostring(disk or ''))
|
||||
if disk ~= '' then
|
||||
pcall(mp.set_property, SELECTED_STORE_PROP, disk)
|
||||
end
|
||||
end
|
||||
|
||||
local _pipeline_helper_started = false
|
||||
local _last_ipc_error = ''
|
||||
local _last_ipc_last_req_json = ''
|
||||
local _last_ipc_last_resp_json = ''
|
||||
|
||||
local function _is_pipeline_helper_ready()
|
||||
local ready = mp.get_property_native(PIPELINE_READY_PROP)
|
||||
local ready = mp.get_property(PIPELINE_READY_PROP)
|
||||
if ready == nil or ready == '' then
|
||||
ready = mp.get_property_native(PIPELINE_READY_PROP)
|
||||
end
|
||||
if not ready then
|
||||
return false
|
||||
end
|
||||
@@ -303,6 +420,7 @@ local function _is_pipeline_helper_ready()
|
||||
return age <= 10
|
||||
end
|
||||
|
||||
-- If it's some other non-empty value, treat as ready.
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -833,13 +951,14 @@ local ensure_pipeline_helper_running
|
||||
local function _run_helper_request_response(req, timeout_seconds)
|
||||
_last_ipc_error = ''
|
||||
if not ensure_pipeline_helper_running() then
|
||||
_lua_log('ipc: helper not ready; cannot execute request')
|
||||
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'
|
||||
return nil
|
||||
end
|
||||
|
||||
do
|
||||
local deadline = mp.get_time() + 3.0
|
||||
-- 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
|
||||
@@ -847,10 +966,9 @@ local function _run_helper_request_response(req, timeout_seconds)
|
||||
mp.wait_event(0.05)
|
||||
end
|
||||
if not _is_pipeline_helper_ready() then
|
||||
local rv = tostring(mp.get_property_native(PIPELINE_READY_PROP))
|
||||
_lua_log('ipc: helper not ready; ready=' .. rv)
|
||||
_last_ipc_error = 'helper not ready (ready=' .. rv .. ')'
|
||||
return nil
|
||||
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
|
||||
|
||||
@@ -914,9 +1032,67 @@ end
|
||||
|
||||
local function _refresh_store_cache(timeout_seconds)
|
||||
ensure_mpv_ipc_server()
|
||||
|
||||
-- First, try reading the pre-computed cached property (set by helper at startup).
|
||||
-- This avoids a request/response timeout if observe_property isn't working.
|
||||
local cached_json = mp.get_property('user-data/medeia-store-choices-cached')
|
||||
_lua_log('stores: cache_read cached_json=' .. tostring(cached_json) .. ' len=' .. tostring(cached_json and #cached_json or 0))
|
||||
|
||||
if cached_json and cached_json ~= '' then
|
||||
-- Try to parse as JSON (may fail if not valid JSON)
|
||||
local ok, cached_resp = pcall(utils.parse_json, cached_json)
|
||||
_lua_log('stores: cache_parse ok=' .. tostring(ok) .. ' resp_type=' .. tostring(type(cached_resp)))
|
||||
|
||||
-- Handle both cases: parsed object OR string (if JSON lib returns string)
|
||||
if ok then
|
||||
-- If parse returned a string, it might still be valid JSON; try parsing again
|
||||
if type(cached_resp) == 'string' then
|
||||
_lua_log('stores: cache_parse returned string, trying again...')
|
||||
ok, cached_resp = pcall(utils.parse_json, cached_resp)
|
||||
_lua_log('stores: cache_parse retry ok=' .. tostring(ok) .. ' resp_type=' .. tostring(type(cached_resp)))
|
||||
end
|
||||
|
||||
-- Now check if we have a table with choices
|
||||
if type(cached_resp) == 'table' and type(cached_resp.choices) == 'table' then
|
||||
local out = {}
|
||||
for _, v in ipairs(cached_resp.choices) do
|
||||
local name = trim(tostring(v or ''))
|
||||
if name ~= '' then
|
||||
out[#out + 1] = name
|
||||
end
|
||||
end
|
||||
_cached_store_names = out
|
||||
_store_cache_loaded = true
|
||||
local preview = ''
|
||||
if #_cached_store_names > 0 then
|
||||
preview = table.concat(_cached_store_names, ', ')
|
||||
end
|
||||
_lua_log('stores: loaded ' .. tostring(#_cached_store_names) .. ' stores from cache: ' .. tostring(preview))
|
||||
return true
|
||||
else
|
||||
_lua_log('stores: cache_parse final type mismatch resp_type=' .. tostring(type(cached_resp)) .. ' choices_type=' .. tostring(cached_resp and type(cached_resp.choices) or 'n/a'))
|
||||
end
|
||||
else
|
||||
_lua_log('stores: cache_parse failed ok=' .. tostring(ok) .. ' resp=' .. tostring(cached_resp))
|
||||
end
|
||||
else
|
||||
_lua_log('stores: cache_empty cached_json=' .. tostring(cached_json))
|
||||
end
|
||||
|
||||
-- Fallback: request fresh store-choices from helper (with timeout).
|
||||
_lua_log('stores: requesting store-choices via helper (fallback)')
|
||||
local resp = _run_helper_request_response({ op = 'store-choices' }, timeout_seconds or 1)
|
||||
if not resp or not resp.success or type(resp.choices) ~= 'table' then
|
||||
_lua_log('stores: failed to load store choices via helper; stderr=' .. tostring(resp and resp.stderr or '') .. ' error=' .. tostring(resp and resp.error or ''))
|
||||
_lua_log(
|
||||
'stores: failed to load store choices via helper; success='
|
||||
.. tostring(resp and resp.success or false)
|
||||
.. ' choices_type='
|
||||
.. tostring(resp and type(resp.choices) or 'nil')
|
||||
.. ' stderr='
|
||||
.. tostring(resp and resp.stderr or '')
|
||||
.. ' error='
|
||||
.. tostring(resp and resp.error or '')
|
||||
)
|
||||
return false
|
||||
end
|
||||
|
||||
@@ -929,7 +1105,11 @@ local function _refresh_store_cache(timeout_seconds)
|
||||
end
|
||||
_cached_store_names = out
|
||||
_store_cache_loaded = true
|
||||
_lua_log('stores: loaded ' .. tostring(#_cached_store_names) .. ' stores via helper')
|
||||
local preview = ''
|
||||
if #_cached_store_names > 0 then
|
||||
preview = table.concat(_cached_store_names, ', ')
|
||||
end
|
||||
_lua_log('stores: loaded ' .. tostring(#_cached_store_names) .. ' stores via helper request: ' .. tostring(preview))
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -946,6 +1126,116 @@ local function _uosc_open_list_picker(menu_type, title, items)
|
||||
end
|
||||
end
|
||||
|
||||
local function _open_store_picker()
|
||||
_ensure_selected_store_loaded()
|
||||
|
||||
local selected = _get_selected_store()
|
||||
local cached_count = (type(_cached_store_names) == 'table') and #_cached_store_names or 0
|
||||
local cached_preview = ''
|
||||
if type(_cached_store_names) == 'table' and #_cached_store_names > 0 then
|
||||
cached_preview = table.concat(_cached_store_names, ', ')
|
||||
end
|
||||
_lua_log(
|
||||
'stores: open picker selected='
|
||||
.. tostring(selected)
|
||||
.. ' cached_count='
|
||||
.. tostring(cached_count)
|
||||
.. ' cached='
|
||||
.. tostring(cached_preview)
|
||||
)
|
||||
|
||||
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
|
||||
local payload = { store = name }
|
||||
items[#items + 1] = {
|
||||
title = name,
|
||||
active = (selected ~= '' and name == selected) and true or false,
|
||||
value = { 'script-message-to', mp.get_script_name(), 'medeia-store-select', utils.format_json(payload) },
|
||||
}
|
||||
end
|
||||
end
|
||||
else
|
||||
items[#items + 1] = {
|
||||
title = 'No stores found',
|
||||
hint = 'Configure stores in config.conf',
|
||||
selectable = false,
|
||||
}
|
||||
end
|
||||
|
||||
return items
|
||||
end
|
||||
|
||||
-- Open immediately with whatever cache we have.
|
||||
_uosc_open_list_picker(STORE_PICKER_MENU_TYPE, 'Store', build_items())
|
||||
|
||||
-- Best-effort refresh; retry briefly to avoid races where the helper isn't
|
||||
-- ready/observing yet at the exact moment the menu opens.
|
||||
local function attempt_refresh(tries_left)
|
||||
local before_count = (type(_cached_store_names) == 'table') and #_cached_store_names or 0
|
||||
local before_preview = ''
|
||||
if type(_cached_store_names) == 'table' and #_cached_store_names > 0 then
|
||||
before_preview = table.concat(_cached_store_names, ', ')
|
||||
end
|
||||
|
||||
local ok = _refresh_store_cache(1.2)
|
||||
local after_count = (type(_cached_store_names) == 'table') and #_cached_store_names or 0
|
||||
local after_preview = ''
|
||||
if type(_cached_store_names) == 'table' and #_cached_store_names > 0 then
|
||||
after_preview = table.concat(_cached_store_names, ', ')
|
||||
end
|
||||
|
||||
_lua_log(
|
||||
'stores: refresh attempt ok='
|
||||
.. tostring(ok)
|
||||
.. ' before='
|
||||
.. tostring(before_count)
|
||||
.. ' after='
|
||||
.. tostring(after_count)
|
||||
.. ' after='
|
||||
.. tostring(after_preview)
|
||||
)
|
||||
|
||||
if after_count > 0 and (after_count ~= before_count or after_preview ~= before_preview) then
|
||||
_lua_log('stores: reopening menu (store list changed)')
|
||||
_uosc_open_list_picker(STORE_PICKER_MENU_TYPE, 'Store', build_items())
|
||||
return
|
||||
end
|
||||
|
||||
if tries_left > 0 then
|
||||
mp.add_timeout(0.25, function()
|
||||
attempt_refresh(tries_left - 1)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
mp.add_timeout(0.05, function()
|
||||
attempt_refresh(6)
|
||||
end)
|
||||
end
|
||||
|
||||
mp.register_script_message('medeia-store-picker', function()
|
||||
_open_store_picker()
|
||||
end)
|
||||
|
||||
mp.register_script_message('medeia-store-select', function(json)
|
||||
local ok, ev = pcall(utils.parse_json, json)
|
||||
if not ok or type(ev) ~= 'table' then
|
||||
return
|
||||
end
|
||||
local store = trim(tostring(ev.store or ''))
|
||||
if store == '' then
|
||||
return
|
||||
end
|
||||
_set_selected_store(store)
|
||||
mp.osd_message('Store: ' .. store, 2)
|
||||
end)
|
||||
|
||||
-- No-op handler for placeholder menu items.
|
||||
mp.register_script_message('medios-nop', function()
|
||||
return
|
||||
@@ -1322,6 +1612,7 @@ end
|
||||
|
||||
-- Prime store cache shortly after load (best-effort; picker also refreshes on-demand).
|
||||
mp.add_timeout(0.10, function()
|
||||
pcall(_ensure_selected_store_loaded)
|
||||
if not _store_cache_loaded then
|
||||
pcall(_refresh_store_cache, 1.5)
|
||||
end
|
||||
@@ -1729,23 +2020,71 @@ if not opts.cli_path then
|
||||
opts.cli_path = find_file_upwards(script_dir, "CLI.py", 6) or "CLI.py"
|
||||
end
|
||||
|
||||
-- Helper to run pipeline
|
||||
function M.run_pipeline(pipeline_cmd, seeds)
|
||||
local out, err = run_pipeline_via_ipc(pipeline_cmd, seeds, 5)
|
||||
if out ~= nil then
|
||||
return out
|
||||
-- Clean API wrapper for executing Python functions from Lua
|
||||
local function _call_mpv_api(request)
|
||||
-- Call the MPV Lua API (mpv_lua_api.py) with a JSON request.
|
||||
-- Returns: JSON-decoded response object with {success, stdout, stderr, error, ...}
|
||||
local request_json = utils.format_json(request)
|
||||
|
||||
-- Try to get log file path; skip if not available
|
||||
local log_file = ''
|
||||
local home = os.getenv('USERPROFILE') or os.getenv('HOME') or ''
|
||||
if home ~= '' then
|
||||
log_file = home .. '/../../medios/Medios-Macina/Log/medeia-mpv-helper.log'
|
||||
end
|
||||
if err ~= nil then
|
||||
local log_path = write_temp_log('medeia-pipeline-error', tostring(err))
|
||||
local suffix = log_path and (' (log: ' .. log_path .. ')') or ''
|
||||
_lua_log('Pipeline error: ' .. tostring(err) .. suffix)
|
||||
mp.osd_message('Error: pipeline failed' .. suffix, 6)
|
||||
return nil
|
||||
|
||||
_lua_log('api: calling mpv_lua_api cmd=' .. tostring(request.cmd))
|
||||
|
||||
local python_exe = _resolve_python_exe(true)
|
||||
if not python_exe or python_exe == '' then
|
||||
_lua_log('api: FAILED - no python exe')
|
||||
return { success = false, error = 'could not find Python' }
|
||||
end
|
||||
|
||||
-- Try to locate API script
|
||||
local api_script = nil
|
||||
local script_dir = mp.get_script_directory()
|
||||
if script_dir and script_dir ~= '' then
|
||||
api_script = script_dir .. '/mpv_lua_api.py'
|
||||
if not utils.file_info(api_script) then
|
||||
api_script = script_dir .. '/../mpv_lua_api.py'
|
||||
end
|
||||
end
|
||||
|
||||
if not api_script or api_script == '' or not utils.file_info(api_script) then
|
||||
-- Fallback: try absolute path
|
||||
local repo_root = os.getenv('USERPROFILE')
|
||||
if repo_root then
|
||||
api_script = repo_root .. '/../../../medios/Medios-Macina/MPV/mpv_lua_api.py'
|
||||
end
|
||||
end
|
||||
|
||||
if not api_script or api_script == '' then
|
||||
_lua_log('api: FAILED - could not locate mpv_lua_api.py')
|
||||
return { success = false, error = 'could not locate mpv_lua_api.py' }
|
||||
end
|
||||
|
||||
_lua_log('api: python=' .. tostring(python_exe) .. ' script=' .. tostring(api_script))
|
||||
|
||||
local res = utils.subprocess({
|
||||
args = { python_exe, api_script, request_json, log_file },
|
||||
cancellable = false,
|
||||
})
|
||||
|
||||
if res and res.status == 0 and res.stdout then
|
||||
local ok, response = pcall(utils.parse_json, res.stdout)
|
||||
if ok and response then
|
||||
_lua_log('api: response success=' .. tostring(response.success))
|
||||
return response
|
||||
else
|
||||
_lua_log('api: failed to parse response: ' .. tostring(res.stdout))
|
||||
return { success = false, error = 'malformed response', stdout = res.stdout }
|
||||
end
|
||||
else
|
||||
local stderr = res and res.stderr or 'unknown error'
|
||||
_lua_log('api: subprocess failed status=' .. tostring(res and res.status or 'nil') .. ' stderr=' .. stderr)
|
||||
return { success = false, error = stderr }
|
||||
end
|
||||
|
||||
mp.osd_message('Error: pipeline helper not available', 6)
|
||||
_lua_log('ipc: helper not available; refusing to spawn python subprocess')
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Helper to run pipeline and parse JSON output
|
||||
@@ -1858,62 +2197,218 @@ end
|
||||
|
||||
-- Prompt for trim range via an input box and callback
|
||||
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 load_err = nil
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
range = trim(tostring(range or ''))
|
||||
_lua_log('trim: after_trim range=' .. tostring(range))
|
||||
|
||||
if range == '' then
|
||||
mp.osd_message('Trim cancelled (no range provided)', 3)
|
||||
_lua_log('trim: CANCELLED - empty range')
|
||||
return
|
||||
end
|
||||
|
||||
local target = _current_target()
|
||||
if not target or target == '' then
|
||||
mp.osd_message('No file to trim', 3)
|
||||
_lua_log('trim: FAILED - no target')
|
||||
return
|
||||
end
|
||||
_lua_log('trim: target=' .. tostring(target))
|
||||
|
||||
local store_hash = _extract_store_hash(target)
|
||||
if store_hash then
|
||||
_lua_log('trim: store_hash detected store=' .. tostring(store_hash.store) .. ' hash=' .. tostring(store_hash.hash))
|
||||
else
|
||||
_lua_log('trim: store_hash=nil (local file)')
|
||||
end
|
||||
|
||||
-- Get the selected store (this reads from saved config or mpv property)
|
||||
_ensure_selected_store_loaded()
|
||||
local selected_store = _get_selected_store()
|
||||
-- Strip any existing quotes from the store name
|
||||
selected_store = selected_store:gsub('^"', ''):gsub('"$', '')
|
||||
_lua_log('trim: selected_store=' .. tostring(selected_store or 'NONE'))
|
||||
_lua_log('trim: _cached_store_names=' .. tostring(_cached_store_names and #_cached_store_names or 0))
|
||||
_lua_log('trim: _selected_store_index=' .. tostring(_selected_store_index or 'nil'))
|
||||
|
||||
-- Prefer the resolved stream URL/filename so trimming can avoid full downloads where possible.
|
||||
local stream = trim(tostring(mp.get_property('stream-open-filename') or ''))
|
||||
if stream == '' then
|
||||
stream = tostring(target)
|
||||
end
|
||||
_lua_log('trim: stream=' .. tostring(stream))
|
||||
|
||||
local pipeline_cmd
|
||||
local title = trim(tostring(mp.get_property('media-title') or ''))
|
||||
if title == '' then
|
||||
title = 'clip'
|
||||
end
|
||||
_lua_log('trim: title=' .. tostring(title))
|
||||
|
||||
-- ===== TRIM IN LUA USING FFMPEG =====
|
||||
mp.osd_message('Starting FFmpeg trim...', 1)
|
||||
_lua_log('trim: calling trim_module.trim_file with range=' .. range)
|
||||
|
||||
-- Get temp directory from config or use default
|
||||
local temp_dir = mp.get_property('user-data/medeia-config-temp') or os.getenv('TEMP') or os.getenv('TMP') or '/tmp'
|
||||
_lua_log('trim: using temp_dir=' .. temp_dir)
|
||||
|
||||
local success, output_path, error_msg = trim_module.trim_file(stream, range, temp_dir)
|
||||
|
||||
if not success then
|
||||
mp.osd_message('Trim failed: ' .. error_msg, 3)
|
||||
_lua_log('trim: FAILED - ' .. error_msg)
|
||||
return
|
||||
end
|
||||
|
||||
_lua_log('trim: FFmpeg SUCCESS - output_path=' .. output_path)
|
||||
mp.osd_message('Trim complete, uploading...', 2)
|
||||
|
||||
-- ===== UPLOAD TO PYTHON FOR STORAGE AND METADATA =====
|
||||
local pipeline_cmd = nil
|
||||
_lua_log('trim: === BUILDING UPLOAD PIPELINE ===')
|
||||
_lua_log('trim: store_hash=' .. tostring(store_hash and (store_hash.store .. '/' .. store_hash.hash) or 'nil'))
|
||||
_lua_log('trim: selected_store=' .. tostring(selected_store or 'nil'))
|
||||
|
||||
if store_hash then
|
||||
pipeline_cmd =
|
||||
'get-tag -emit -store ' .. quote_pipeline_arg(store_hash.store) ..
|
||||
' -query ' .. quote_pipeline_arg('hash:' .. store_hash.hash) ..
|
||||
' | trim-file -input ' .. quote_pipeline_arg(stream) ..
|
||||
' -range ' .. quote_pipeline_arg(range) ..
|
||||
' | add-file -store ' .. quote_pipeline_arg(store_hash.store)
|
||||
else
|
||||
if utils.file_info(tostring(target)) then
|
||||
pipeline_cmd = 'trim-file -path ' .. quote_pipeline_arg(target) .. ' -range ' .. quote_pipeline_arg(range)
|
||||
-- Original file is from a store - set relationship to it
|
||||
_lua_log('trim: building store file pipeline (original from store)')
|
||||
if selected_store then
|
||||
pipeline_cmd =
|
||||
'get-tag -emit -store ' .. quote_pipeline_arg(store_hash.store) ..
|
||||
' -query ' .. quote_pipeline_arg('hash:' .. store_hash.hash) ..
|
||||
' | add-file -path ' .. quote_pipeline_arg(output_path) ..
|
||||
' -store "' .. selected_store .. '"' ..
|
||||
' | add-relationship -store "' .. selected_store .. '"' ..
|
||||
' -to-hash ' .. quote_pipeline_arg(store_hash.hash)
|
||||
else
|
||||
pipeline_cmd = 'trim-file -input ' .. quote_pipeline_arg(stream) .. ' -range ' .. quote_pipeline_arg(range)
|
||||
pipeline_cmd =
|
||||
'get-tag -emit -store ' .. quote_pipeline_arg(store_hash.store) ..
|
||||
' -query ' .. quote_pipeline_arg('hash:' .. store_hash.hash) ..
|
||||
' | add-file -path ' .. quote_pipeline_arg(output_path) ..
|
||||
' -store "' .. store_hash.store .. '"' ..
|
||||
' | add-relationship -store "' .. store_hash.store .. '"' ..
|
||||
' -to-hash ' .. quote_pipeline_arg(store_hash.hash)
|
||||
end
|
||||
else
|
||||
-- Local file: save to selected store if available
|
||||
_lua_log('trim: local file pipeline (not from store)')
|
||||
if selected_store then
|
||||
_lua_log('trim: building add-file command to selected_store=' .. selected_store)
|
||||
-- Don't add title if empty - the file path will be used as title by default
|
||||
pipeline_cmd = 'add-file -path ' .. quote_pipeline_arg(output_path) ..
|
||||
' -store "' .. selected_store .. '"'
|
||||
_lua_log('trim: pipeline_cmd=' .. pipeline_cmd)
|
||||
else
|
||||
mp.osd_message('Trim complete: ' .. output_path, 5)
|
||||
_lua_log('trim: no store selected, trim complete at ' .. output_path)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if not _run_pipeline_detached(pipeline_cmd) then
|
||||
M.run_pipeline(pipeline_cmd)
|
||||
if not pipeline_cmd or pipeline_cmd == '' then
|
||||
mp.osd_message('Trim error: could not build upload command', 3)
|
||||
_lua_log('trim: FAILED - empty pipeline_cmd')
|
||||
return
|
||||
end
|
||||
|
||||
_lua_log('trim: final upload_cmd=' .. pipeline_cmd)
|
||||
_lua_log('trim: === CALLING API FOR UPLOAD ===')
|
||||
|
||||
-- Call the API to handle metadata/storage
|
||||
local response = _call_mpv_api({
|
||||
cmd = 'execute_pipeline',
|
||||
pipeline = pipeline_cmd,
|
||||
})
|
||||
|
||||
_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 = response.error or response.stderr or 'unknown error'
|
||||
mp.osd_message('Upload failed: ' .. err_msg, 5)
|
||||
_lua_log('trim: upload FAILED - ' .. err_msg)
|
||||
end
|
||||
mp.osd_message('Trim started', 3)
|
||||
end
|
||||
|
||||
function M.open_trim_prompt()
|
||||
_lua_log('=== OPEN_TRIM_PROMPT called')
|
||||
|
||||
local marker_range = _get_trim_range_from_clip_markers()
|
||||
_lua_log('trim_prompt: marker_range=' .. tostring(marker_range or 'NONE'))
|
||||
|
||||
if marker_range then
|
||||
_lua_log('trim_prompt: using auto-detected markers, starting trim')
|
||||
mp.osd_message('Using clip markers: ' .. marker_range, 2)
|
||||
_start_trim_with_range(marker_range)
|
||||
return
|
||||
end
|
||||
|
||||
_lua_log('trim_prompt: no clip markers detected, showing prompt')
|
||||
mp.osd_message('Set 2 clip markers with the marker button, or enter range manually', 3)
|
||||
|
||||
local selected_store = _cached_store_names and #_cached_store_names > 0 and _selected_store_index
|
||||
and _cached_store_names[_selected_store_index] or nil
|
||||
local store_hint = selected_store and ' (saving to: ' .. selected_store .. ')' or ' (no store selected; will save locally)'
|
||||
|
||||
local menu_data = {
|
||||
type = TRIM_PROMPT_MENU_TYPE,
|
||||
title = 'Trim file',
|
||||
search_style = 'palette',
|
||||
search_debounce = 'submit',
|
||||
on_search = 'callback',
|
||||
footnote = "Enter time range (e.g. '00:03:45-00:03:55' or '1h3m-1h10m30s') and press Enter",
|
||||
footnote = "Enter time range (e.g. '00:03:45-00:03:55' or '1h3m-1h10m30s') and press Enter" .. store_hint,
|
||||
callback = { mp.get_script_name(), 'medios-trim-run' },
|
||||
items = {
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user