fd
Some checks failed
smoke-mm / Install & smoke test mm --help (push) Has been cancelled

This commit is contained in:
2025-12-27 03:13:16 -08:00
parent a595453a9b
commit 71b542ae91
8 changed files with 1069 additions and 57 deletions

View File

@@ -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 = {
{