mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-03-01 18:22:41 -08:00
feat(launcher): pause auto-start playback until overlay is ready
This commit is contained in:
@@ -31,6 +31,7 @@ function M.create(ctx)
|
||||
|
||||
local function on_file_loaded()
|
||||
aniskip.clear_aniskip_state()
|
||||
process.disarm_auto_play_ready_gate()
|
||||
|
||||
local should_auto_start = resolve_auto_start_enabled()
|
||||
if should_auto_start then
|
||||
@@ -59,6 +60,7 @@ function M.create(ctx)
|
||||
local function on_shutdown()
|
||||
aniskip.clear_aniskip_state()
|
||||
hover.clear_hover_overlay()
|
||||
process.disarm_auto_play_ready_gate()
|
||||
if state.overlay_running or state.texthooker_running then
|
||||
subminer_log("info", "lifecycle", "mpv shutting down, stopping SubMiner process")
|
||||
show_osd("Shutting down...")
|
||||
@@ -73,6 +75,7 @@ function M.create(ctx)
|
||||
hover.clear_hover_overlay()
|
||||
end)
|
||||
mp.register_event("end-file", function()
|
||||
process.disarm_auto_play_ready_gate()
|
||||
hover.clear_hover_overlay()
|
||||
end)
|
||||
mp.register_event("shutdown", function()
|
||||
|
||||
@@ -45,7 +45,14 @@ function M.create(ctx)
|
||||
|
||||
local function show_osd(message)
|
||||
if opts.osd_messages then
|
||||
mp.osd_message("SubMiner: " .. message, 3)
|
||||
local payload = "SubMiner: " .. message
|
||||
local sent = false
|
||||
if type(mp.osd_message) == "function" then
|
||||
sent = pcall(mp.osd_message, payload, 3)
|
||||
end
|
||||
if not sent and type(mp.commandv) == "function" then
|
||||
pcall(mp.commandv, "show-text", payload, "3000")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -29,6 +29,9 @@ function M.create(ctx)
|
||||
mp.register_script_message("subminer-status", function()
|
||||
process.check_status()
|
||||
end)
|
||||
mp.register_script_message("subminer-autoplay-ready", function()
|
||||
process.notify_auto_play_ready()
|
||||
end)
|
||||
mp.register_script_message("subminer-aniskip-refresh", function()
|
||||
aniskip.fetch_aniskip_for_current_media("script-message")
|
||||
end)
|
||||
|
||||
@@ -8,7 +8,8 @@ function M.load(options_lib, default_socket_path)
|
||||
texthooker_port = 5174,
|
||||
backend = "auto",
|
||||
auto_start = true,
|
||||
auto_start_visible_overlay = false,
|
||||
auto_start_visible_overlay = true,
|
||||
auto_start_pause_until_ready = true,
|
||||
osd_messages = true,
|
||||
log_level = "info",
|
||||
aniskip_enabled = true,
|
||||
|
||||
@@ -2,6 +2,7 @@ local M = {}
|
||||
|
||||
local OVERLAY_START_RETRY_DELAY_SECONDS = 0.2
|
||||
local OVERLAY_START_MAX_ATTEMPTS = 6
|
||||
local AUTO_PLAY_READY_TIMEOUT_SECONDS = 15
|
||||
|
||||
function M.create(ctx)
|
||||
local mp = ctx.mp
|
||||
@@ -22,6 +23,14 @@ function M.create(ctx)
|
||||
return options_helper.coerce_bool(raw_visible_overlay, false)
|
||||
end
|
||||
|
||||
local function resolve_pause_until_ready()
|
||||
local raw_pause_until_ready = opts.auto_start_pause_until_ready
|
||||
if raw_pause_until_ready == nil then
|
||||
raw_pause_until_ready = opts["auto-start-pause-until-ready"]
|
||||
end
|
||||
return options_helper.coerce_bool(raw_pause_until_ready, false)
|
||||
end
|
||||
|
||||
local function normalize_socket_path(path)
|
||||
if type(path) ~= "string" then
|
||||
return nil
|
||||
@@ -53,6 +62,54 @@ function M.create(ctx)
|
||||
return selected
|
||||
end
|
||||
|
||||
local function clear_auto_play_ready_timeout()
|
||||
local timeout = state.auto_play_ready_timeout
|
||||
if timeout and timeout.kill then
|
||||
timeout:kill()
|
||||
end
|
||||
state.auto_play_ready_timeout = nil
|
||||
end
|
||||
|
||||
local function disarm_auto_play_ready_gate()
|
||||
clear_auto_play_ready_timeout()
|
||||
state.auto_play_ready_gate_armed = false
|
||||
end
|
||||
|
||||
local function release_auto_play_ready_gate(reason)
|
||||
if not state.auto_play_ready_gate_armed then
|
||||
return
|
||||
end
|
||||
disarm_auto_play_ready_gate()
|
||||
mp.set_property_native("pause", false)
|
||||
show_osd("Subtitle annotations loaded")
|
||||
subminer_log("info", "process", "Resuming playback after startup gate: " .. tostring(reason or "ready"))
|
||||
end
|
||||
|
||||
local function arm_auto_play_ready_gate()
|
||||
if state.auto_play_ready_gate_armed then
|
||||
clear_auto_play_ready_timeout()
|
||||
end
|
||||
state.auto_play_ready_gate_armed = true
|
||||
mp.set_property_native("pause", true)
|
||||
show_osd("Loading subtitle annotations...")
|
||||
subminer_log("info", "process", "Pausing playback until SubMiner overlay/tokenization readiness signal")
|
||||
state.auto_play_ready_timeout = mp.add_timeout(AUTO_PLAY_READY_TIMEOUT_SECONDS, function()
|
||||
if not state.auto_play_ready_gate_armed then
|
||||
return
|
||||
end
|
||||
subminer_log(
|
||||
"warn",
|
||||
"process",
|
||||
"Startup readiness signal timed out; resuming playback to avoid stalled pause"
|
||||
)
|
||||
release_auto_play_ready_gate("timeout")
|
||||
end)
|
||||
end
|
||||
|
||||
local function notify_auto_play_ready()
|
||||
release_auto_play_ready_gate("tokenization-ready")
|
||||
end
|
||||
|
||||
local function build_command_args(action, overrides)
|
||||
overrides = overrides or {}
|
||||
local args = { state.binary_path }
|
||||
@@ -75,14 +132,15 @@ function M.create(ctx)
|
||||
table.insert(args, "--socket")
|
||||
table.insert(args, socket_path)
|
||||
|
||||
local should_show_visible = resolve_visible_overlay_startup()
|
||||
if should_show_visible and overrides.auto_start_trigger == true then
|
||||
should_show_visible = has_matching_mpv_ipc_socket(socket_path)
|
||||
end
|
||||
if should_show_visible then
|
||||
table.insert(args, "--show-visible-overlay")
|
||||
else
|
||||
table.insert(args, "--hide-visible-overlay")
|
||||
-- Keep auto-start --start requests idempotent for second-instance handling.
|
||||
-- Visibility is applied as a separate control command after startup.
|
||||
if overrides.auto_start_trigger ~= true then
|
||||
local should_show_visible = resolve_visible_overlay_startup()
|
||||
if should_show_visible then
|
||||
table.insert(args, "--show-visible-overlay")
|
||||
else
|
||||
table.insert(args, "--hide-visible-overlay")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -182,6 +240,8 @@ function M.create(ctx)
|
||||
end
|
||||
|
||||
local function start_overlay(overrides)
|
||||
overrides = overrides or {}
|
||||
|
||||
if not binary.ensure_binary_available() then
|
||||
subminer_log("error", "binary", "SubMiner binary not found")
|
||||
show_osd("Error: binary not found")
|
||||
@@ -189,16 +249,31 @@ function M.create(ctx)
|
||||
end
|
||||
|
||||
if state.overlay_running then
|
||||
if overrides.auto_start_trigger == true then
|
||||
subminer_log("debug", "process", "Auto-start ignored because overlay is already running")
|
||||
return
|
||||
end
|
||||
subminer_log("info", "process", "Overlay already running")
|
||||
show_osd("Already running")
|
||||
return
|
||||
end
|
||||
|
||||
overrides = overrides or {}
|
||||
local texthooker_enabled = overrides.texthooker_enabled
|
||||
if texthooker_enabled == nil then
|
||||
texthooker_enabled = opts.texthooker_enabled
|
||||
end
|
||||
local socket_path = overrides.socket_path or opts.socket_path
|
||||
local should_pause_until_ready = (
|
||||
overrides.auto_start_trigger == true
|
||||
and resolve_visible_overlay_startup()
|
||||
and resolve_pause_until_ready()
|
||||
and has_matching_mpv_ipc_socket(socket_path)
|
||||
)
|
||||
if should_pause_until_ready then
|
||||
arm_auto_play_ready_gate()
|
||||
else
|
||||
disarm_auto_play_ready_gate()
|
||||
end
|
||||
|
||||
local function launch_overlay_with_retry(attempt)
|
||||
local args = build_command_args("start", overrides)
|
||||
@@ -236,9 +311,19 @@ function M.create(ctx)
|
||||
state.overlay_running = false
|
||||
subminer_log("error", "process", "Overlay start failed after retries: " .. reason)
|
||||
show_osd("Overlay start failed")
|
||||
release_auto_play_ready_gate("overlay-start-failed")
|
||||
return
|
||||
end
|
||||
|
||||
if overrides.auto_start_trigger == true then
|
||||
local visibility_action = resolve_visible_overlay_startup()
|
||||
and "show-visible-overlay"
|
||||
or "hide-visible-overlay"
|
||||
run_control_command_async(visibility_action, {
|
||||
log_level = overrides.log_level,
|
||||
})
|
||||
end
|
||||
|
||||
end)
|
||||
end
|
||||
|
||||
@@ -277,6 +362,7 @@ function M.create(ctx)
|
||||
|
||||
state.overlay_running = false
|
||||
state.texthooker_running = false
|
||||
disarm_auto_play_ready_gate()
|
||||
show_osd("Stopped")
|
||||
end
|
||||
|
||||
@@ -326,6 +412,7 @@ function M.create(ctx)
|
||||
run_control_command_async("stop", nil, function()
|
||||
state.overlay_running = false
|
||||
state.texthooker_running = false
|
||||
disarm_auto_play_ready_gate()
|
||||
|
||||
ensure_texthooker_running(function()
|
||||
local start_args = build_command_args("start")
|
||||
@@ -384,6 +471,8 @@ function M.create(ctx)
|
||||
restart_overlay = restart_overlay,
|
||||
check_status = check_status,
|
||||
check_binary_available = check_binary_available,
|
||||
notify_auto_play_ready = notify_auto_play_ready,
|
||||
disarm_auto_play_ready_gate = disarm_auto_play_ready_gate,
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
@@ -27,6 +27,8 @@ function M.new()
|
||||
found = false,
|
||||
prompt_shown = false,
|
||||
},
|
||||
auto_play_ready_gate_armed = false,
|
||||
auto_play_ready_timeout = nil,
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user