This commit is contained in:
2026-03-21 22:56:37 -07:00
parent b183167a64
commit f8c98b39bd
4 changed files with 304 additions and 82 deletions

View File

@@ -4,7 +4,7 @@ local msg = require 'mp.msg'
local M = {}
local MEDEIA_LUA_VERSION = '2026-03-21.1'
local MEDEIA_LUA_VERSION = '2026-03-21.3'
-- Expose a tiny breadcrumb for debugging which script version is loaded.
pcall(mp.set_property, 'user-data/medeia-lua-version', MEDEIA_LUA_VERSION)
@@ -748,6 +748,7 @@ local _current_url_for_web_actions
local _store_status_hint_for_url
local _refresh_current_store_url_status
local _skip_next_store_check_url = ''
local _pick_folder_windows
local function _normalize_store_name(store)
store = trim(tostring(store or ''))
@@ -755,6 +756,22 @@ local function _normalize_store_name(store)
return trim(store)
end
local function _is_cached_store_name(store)
local needle = _normalize_store_name(store)
if needle == '' then
return false
end
if type(_cached_store_names) ~= 'table' then
return false
end
for _, name in ipairs(_cached_store_names) do
if _normalize_store_name(name) == needle then
return true
end
end
return false
end
local function _get_script_opts_dir()
local dir = nil
pcall(function()
@@ -863,7 +880,7 @@ local function _is_pipeline_helper_ready()
helper_version = mp.get_property_native('user-data/medeia-pipeline-helper-version')
end
helper_version = tostring(helper_version or '')
if helper_version ~= '2026-03-22.2' then
if helper_version ~= '2026-03-22.4' then
return false
end
@@ -930,7 +947,7 @@ local function _helper_ready_diagnostics()
end
return 'ready=' .. tostring(ready or '')
.. ' helper_version=' .. tostring(helper_version or '')
.. ' required_version=2026-03-22.2'
.. ' required_version=2026-03-22.4'
.. ' last_value=' .. tostring(_helper_ready_last_value or '')
.. ' last_seen_age=' .. tostring(age)
end
@@ -973,11 +990,28 @@ end
local function attempt_start_pipeline_helper_async(callback)
-- Async version: spawn helper without blocking UI. Calls callback(success) when done.
callback = callback or function() end
local helper_start_state = M._helper_start_state or { inflight = false, callbacks = {} }
M._helper_start_state = helper_start_state
local function finish(success)
local callbacks = helper_start_state.callbacks or {}
helper_start_state.callbacks = {}
helper_start_state.inflight = false
for _, cb in ipairs(callbacks) do
pcall(cb, success)
end
end
if _is_pipeline_helper_ready() then
callback(true)
return
end
if helper_start_state.inflight then
table.insert(helper_start_state.callbacks, callback)
_lua_log('attempt_start_pipeline_helper_async: join existing startup')
return
end
-- Debounce: don't spawn multiple helpers in quick succession
local now = mp.get_time()
@@ -987,6 +1021,8 @@ local function attempt_start_pipeline_helper_async(callback)
return
end
_helper_start_debounce_ts = now
helper_start_state.inflight = true
helper_start_state.callbacks = { callback }
-- Clear any stale ready heartbeat from an earlier helper instance before spawning.
pcall(mp.set_property, PIPELINE_READY_PROP, '')
@@ -997,7 +1033,7 @@ local function attempt_start_pipeline_helper_async(callback)
local python = _resolve_python_exe(true)
if not python or python == '' then
_lua_log('attempt_start_pipeline_helper_async: no python executable available')
callback(false)
finish(false)
return
end
@@ -1015,7 +1051,7 @@ local function attempt_start_pipeline_helper_async(callback)
if not ok then
_lua_log('attempt_start_pipeline_helper_async: spawn failed final=' .. tostring(detail or _describe_subprocess_result(result)))
callback(false)
finish(false)
return
end
@@ -1026,13 +1062,13 @@ local function attempt_start_pipeline_helper_async(callback)
if _is_pipeline_helper_ready() then
timer:kill()
_lua_log('attempt_start_pipeline_helper_async: helper ready')
callback(true)
finish(true)
return
end
if mp.get_time() >= deadline then
timer:kill()
_lua_log('attempt_start_pipeline_helper_async: timeout waiting for ready')
callback(false)
finish(false)
end
end)
end
@@ -1954,6 +1990,7 @@ local function _start_screenshot_store_save(store, out_path, tags)
return false
end
local is_named_store = _is_cached_store_name(store)
local tag_list = _normalize_tag_list(tags)
local screenshot_url = trim(tostring((_current_url_for_web_actions and _current_url_for_web_actions()) or mp.get_property(CURRENT_WEB_URL_PROP) or ''))
if screenshot_url == '' or not screenshot_url:match('^https?://') then
@@ -1961,29 +1998,35 @@ local function _start_screenshot_store_save(store, out_path, tags)
end
local cmd = 'add-file -store ' .. quote_pipeline_arg(store)
.. ' -path ' .. quote_pipeline_arg(out_path)
_set_selected_store(store)
if screenshot_url ~= '' then
cmd = cmd .. ' -url ' .. quote_pipeline_arg(screenshot_url)
end
if is_named_store then
_set_selected_store(store)
end
local tag_suffix = (#tag_list > 0) and (' | tags: ' .. tostring(#tag_list)) or ''
if #tag_list > 0 then
local tag_string = table.concat(tag_list, ',')
cmd = cmd .. ' | add-tag ' .. quote_pipeline_arg(tag_string)
end
if screenshot_url ~= '' then
cmd = cmd .. ' | add-url ' .. quote_pipeline_arg(screenshot_url)
end
local queue_target = is_named_store and ('store ' .. store) or 'folder'
local success_text = is_named_store and ('Screenshot saved to store: ' .. store .. tag_suffix) or ('Screenshot saved to folder' .. tag_suffix)
local failure_text = is_named_store and 'Screenshot upload failed' or 'Screenshot save failed'
_lua_log('screenshot-save: queueing repl pipeline cmd=' .. cmd)
return _queue_pipeline_in_repl(
cmd,
'Queued in REPL: screenshot -> ' .. store .. tag_suffix,
'Queued in REPL: screenshot -> ' .. queue_target .. tag_suffix,
'Screenshot queue failed',
'screenshot-save',
{
kind = 'mpv-screenshot',
mpv_notify = {
success_text = 'Screenshot saved to store: ' .. store .. tag_suffix,
failure_text = 'Screenshot upload failed',
success_text = success_text,
failure_text = failure_text,
duration_ms = 3500,
},
}
@@ -2049,6 +2092,14 @@ local function _open_store_picker_for_pending_screenshot()
local selected = _get_selected_store()
local items = {}
if _is_windows() then
items[#items + 1] = {
title = 'Pick folder…',
hint = 'Save screenshot to a local folder',
value = { 'script-message-to', mp.get_script_name(), 'medeia-image-screenshot-pick-path', '{}' },
}
end
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 ''))
@@ -2061,7 +2112,7 @@ local function _open_store_picker_for_pending_screenshot()
}
end
end
else
elseif #items == 0 then
items[#items + 1] = {
title = 'No stores found',
hint = 'Configure stores in config.conf',
@@ -2149,6 +2200,9 @@ local function _capture_screenshot()
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 not _is_cached_store_name(selected_store) then
selected_store = ''
end
if store_count > 1 then
_pending_screenshot = { path = out_path }
@@ -2200,6 +2254,24 @@ mp.register_script_message('medeia-image-screenshot-pick-store', function(json)
_open_screenshot_tag_prompt(store, out_path)
end)
mp.register_script_message('medeia-image-screenshot-pick-path', function()
if type(_pending_screenshot) ~= 'table' or not _pending_screenshot.path then
return
end
if not _is_windows() then
mp.osd_message('Folder picker is Windows-only', 4)
return
end
local folder = _pick_folder_windows()
if not folder or folder == '' then
return
end
local out_path = tostring(_pending_screenshot.path or '')
_open_screenshot_tag_prompt(folder, out_path)
end)
mp.register_script_message('medeia-image-screenshot-tags-search', function(query)
_apply_screenshot_tag_query(query)
end)
@@ -2591,7 +2663,7 @@ local function _extract_store_hash(target)
return nil
end
local function _pick_folder_windows()
_pick_folder_windows = function()
-- Native folder picker via PowerShell + WinForms.
local ps = [[Add-Type -AssemblyName System.Windows.Forms; $d = New-Object System.Windows.Forms.FolderBrowserDialog; $d.Description = 'Select download folder'; $d.ShowNewFolderButton = $true; if ($d.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) { $d.SelectedPath }]]
local res = utils.subprocess({