mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-05-26 00:55:16 -07:00
feat(config): add configuration window (#70)
This commit is contained in:
+3
-75
@@ -1,75 +1,3 @@
|
||||
# SubMiner configuration
|
||||
# Place this file in ~/.config/mpv/script-opts/
|
||||
|
||||
# Path to SubMiner binary (leave empty for auto-detection)
|
||||
# Auto-detection searches common locations, including:
|
||||
# - macOS: /Applications/SubMiner.app/Contents/MacOS/SubMiner, ~/Applications/SubMiner.app/Contents/MacOS/SubMiner
|
||||
# - Windows: %LOCALAPPDATA%\Programs\SubMiner\SubMiner.exe, %ProgramFiles%\SubMiner\SubMiner.exe
|
||||
# - Linux: ~/.local/bin/SubMiner.AppImage, /opt/SubMiner/SubMiner.AppImage, /usr/local/bin/SubMiner, /usr/local/bin/subminer, /usr/bin/SubMiner, /usr/bin/subminer
|
||||
binary_path=
|
||||
|
||||
# Path to mpv IPC socket (must match input-ipc-server in mpv.conf)
|
||||
# Windows installs rewrite this to \\.\pipe\subminer-socket during installation.
|
||||
socket_path=/tmp/subminer-socket
|
||||
|
||||
# Enable texthooker WebSocket server
|
||||
texthooker_enabled=yes
|
||||
|
||||
# Texthooker WebSocket port
|
||||
texthooker_port=5174
|
||||
|
||||
# Window manager backend: auto, hyprland, sway, x11, macos, windows
|
||||
# "auto" detects based on environment variables
|
||||
backend=auto
|
||||
|
||||
# Automatically start overlay when a file is loaded
|
||||
# Runs only when mpv input-ipc-server matches socket_path.
|
||||
auto_start=yes
|
||||
|
||||
# Automatically show visible overlay when overlay starts
|
||||
# Runs only when mpv input-ipc-server matches socket_path.
|
||||
auto_start_visible_overlay=yes
|
||||
|
||||
# Pause mpv on visible auto-start until SubMiner signals overlay/tokenization readiness.
|
||||
# Requires auto_start=yes and auto_start_visible_overlay=yes.
|
||||
auto_start_pause_until_ready=yes
|
||||
|
||||
# Show OSD messages for overlay status
|
||||
osd_messages=yes
|
||||
|
||||
# Log level for plugin and SubMiner binary: debug, info, warn, error
|
||||
log_level=info
|
||||
|
||||
# Enable AniSkip intro detection + markers.
|
||||
aniskip_enabled=yes
|
||||
|
||||
# Force title (optional). Launcher fills this from guessit when available.
|
||||
aniskip_title=
|
||||
|
||||
# Force season (optional). Launcher fills this from guessit when available.
|
||||
aniskip_season=
|
||||
|
||||
# Force MAL id (optional). Leave blank for title lookup.
|
||||
aniskip_mal_id=
|
||||
|
||||
# Force episode number (optional). Leave blank for filename/title detection.
|
||||
aniskip_episode=
|
||||
|
||||
# Optional pre-fetched AniSkip payload for this media (JSON or base64 JSON). When set, the plugin uses this directly and skips network lookup.
|
||||
aniskip_payload=
|
||||
|
||||
# Show intro skip OSD button while inside OP range.
|
||||
aniskip_show_button=yes
|
||||
|
||||
# OSD text shown for intro skip action.
|
||||
# `%s` is replaced by keybinding.
|
||||
aniskip_button_text=You can skip by pressing %s
|
||||
|
||||
# Keybinding to execute intro skip when button is visible.
|
||||
aniskip_button_key=TAB
|
||||
|
||||
# OSD hint duration in seconds (shown during first 3s of intro).
|
||||
aniskip_button_duration=3
|
||||
|
||||
# MPV keybindings provided by plugin/subminer/main.lua:
|
||||
# y-s start, y-S stop, y-t toggle visible overlay
|
||||
# SubMiner managed playback config lives in SubMiner config.jsonc.
|
||||
# This file is intentionally empty so installed/default mpv script-opts do not
|
||||
# override the app config modal or generated config file.
|
||||
|
||||
@@ -18,8 +18,14 @@ function M.create(ctx)
|
||||
|
||||
local function is_macos()
|
||||
local platform = mp.get_property("platform") or ""
|
||||
if platform == "macos" or platform == "darwin" then
|
||||
return true
|
||||
if platform ~= "" then
|
||||
local normalized = platform:lower()
|
||||
if normalized == "macos" or normalized == "darwin" or normalized == "osx" then
|
||||
return true
|
||||
end
|
||||
if normalized == "windows" or normalized == "win32" or normalized == "linux" then
|
||||
return false
|
||||
end
|
||||
end
|
||||
local ostype = os.getenv("OSTYPE") or ""
|
||||
return ostype:find("darwin") ~= nil
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
local M = {}
|
||||
|
||||
local AUTO_START_SOCKET_RETRY_DELAY_SECONDS = 0.2
|
||||
local AUTO_START_SOCKET_RETRY_MAX_ATTEMPTS = 25
|
||||
|
||||
function M.create(ctx)
|
||||
local mp = ctx.mp
|
||||
local opts = ctx.opts
|
||||
@@ -52,24 +55,95 @@ function M.create(ctx)
|
||||
return options_helper.coerce_bool(raw_auto_start, false)
|
||||
end
|
||||
|
||||
local function rearm_managed_subtitle_defaults()
|
||||
local function next_auto_start_retry_generation()
|
||||
state.auto_start_retry_generation = (state.auto_start_retry_generation or 0) + 1
|
||||
return state.auto_start_retry_generation
|
||||
end
|
||||
|
||||
local function has_matching_subminer_socket()
|
||||
if not process.has_matching_mpv_ipc_socket(opts.socket_path) then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function rearm_managed_subtitle_load_defaults()
|
||||
if not has_matching_subminer_socket() then
|
||||
return false
|
||||
end
|
||||
mp.set_property_native("sub-auto", "fuzzy")
|
||||
mp.set_property_native("sid", "auto")
|
||||
mp.set_property_native("secondary-sid", "auto")
|
||||
return true
|
||||
end
|
||||
|
||||
local function refresh_managed_subtitle_autoloading()
|
||||
if not has_matching_subminer_socket() then
|
||||
return false
|
||||
end
|
||||
mp.set_property_native("sub-auto", "fuzzy")
|
||||
return true
|
||||
end
|
||||
|
||||
local function start_overlay_when_socket_ready(generation, media_identity, same_media_loaded, attempt)
|
||||
if generation ~= state.auto_start_retry_generation then
|
||||
return
|
||||
end
|
||||
if media_identity ~= nil and state.current_media_identity ~= media_identity then
|
||||
return
|
||||
end
|
||||
if not resolve_auto_start_enabled() then
|
||||
schedule_aniskip_fetch("file-loaded", 0)
|
||||
return
|
||||
end
|
||||
|
||||
local has_matching_socket = refresh_managed_subtitle_autoloading()
|
||||
if not has_matching_socket then
|
||||
if attempt < AUTO_START_SOCKET_RETRY_MAX_ATTEMPTS then
|
||||
mp.add_timeout(AUTO_START_SOCKET_RETRY_DELAY_SECONDS, function()
|
||||
start_overlay_when_socket_ready(generation, media_identity, same_media_loaded, attempt + 1)
|
||||
end)
|
||||
return
|
||||
end
|
||||
subminer_log(
|
||||
"info",
|
||||
"lifecycle",
|
||||
"Skipping auto-start: input-ipc-server does not match configured socket_path"
|
||||
)
|
||||
schedule_aniskip_fetch("file-loaded", 0)
|
||||
return
|
||||
end
|
||||
|
||||
process.start_overlay({
|
||||
auto_start_trigger = true,
|
||||
socket_path = opts.socket_path,
|
||||
rearm_pause_until_ready = not same_media_loaded,
|
||||
})
|
||||
-- Give the overlay process a moment to initialize before querying AniSkip.
|
||||
schedule_aniskip_fetch("overlay-start", 0.8)
|
||||
end
|
||||
|
||||
local function on_start_file()
|
||||
if state.pending_reload_media_identity ~= nil then
|
||||
return
|
||||
end
|
||||
rearm_managed_subtitle_load_defaults()
|
||||
end
|
||||
|
||||
local function on_file_loaded()
|
||||
local media_identity = resolve_media_identity()
|
||||
local retry_generation = next_auto_start_retry_generation()
|
||||
local previous_media_identity = state.current_media_identity
|
||||
local same_media_reload = (
|
||||
media_identity ~= nil
|
||||
and state.pending_reload_media_identity ~= nil
|
||||
and media_identity == state.pending_reload_media_identity
|
||||
)
|
||||
local same_media_loaded = (
|
||||
media_identity ~= nil
|
||||
and previous_media_identity ~= nil
|
||||
and media_identity == previous_media_identity
|
||||
)
|
||||
state.pending_reload_media_identity = nil
|
||||
state.current_media_identity = media_identity
|
||||
|
||||
@@ -92,32 +166,18 @@ function M.create(ctx)
|
||||
if not preserve_active_auto_start_gate then
|
||||
process.disarm_auto_play_ready_gate()
|
||||
end
|
||||
has_matching_socket = rearm_managed_subtitle_defaults()
|
||||
|
||||
if should_auto_start then
|
||||
if not has_matching_socket then
|
||||
subminer_log(
|
||||
"info",
|
||||
"lifecycle",
|
||||
"Skipping auto-start: input-ipc-server does not match configured socket_path"
|
||||
)
|
||||
schedule_aniskip_fetch("file-loaded", 0)
|
||||
return
|
||||
end
|
||||
|
||||
process.start_overlay({
|
||||
auto_start_trigger = true,
|
||||
socket_path = opts.socket_path,
|
||||
})
|
||||
-- Give the overlay process a moment to initialize before querying AniSkip.
|
||||
schedule_aniskip_fetch("overlay-start", 0.8)
|
||||
start_overlay_when_socket_ready(retry_generation, media_identity, same_media_loaded, 1)
|
||||
return
|
||||
end
|
||||
|
||||
refresh_managed_subtitle_autoloading()
|
||||
schedule_aniskip_fetch("file-loaded", 0)
|
||||
end
|
||||
|
||||
local function on_shutdown()
|
||||
next_auto_start_retry_generation()
|
||||
aniskip.clear_aniskip_state()
|
||||
hover.clear_hover_overlay()
|
||||
process.disarm_auto_play_ready_gate()
|
||||
@@ -126,6 +186,7 @@ function M.create(ctx)
|
||||
end
|
||||
|
||||
local function register_lifecycle_hooks()
|
||||
mp.register_event("start-file", on_start_file)
|
||||
mp.register_event("file-loaded", on_file_loaded)
|
||||
mp.register_event("shutdown", on_shutdown)
|
||||
mp.register_event("file-loaded", function()
|
||||
@@ -139,6 +200,8 @@ function M.create(ctx)
|
||||
state.pending_reload_media_identity = state.current_media_identity or resolve_media_identity()
|
||||
return
|
||||
end
|
||||
next_auto_start_retry_generation()
|
||||
state.current_media_identity = nil
|
||||
state.pending_reload_media_identity = nil
|
||||
if state.overlay_running and reason ~= "quit" then
|
||||
process.hide_visible_overlay()
|
||||
|
||||
@@ -27,16 +27,16 @@ function M.load(options_lib, default_socket_path)
|
||||
local opts = {
|
||||
binary_path = "",
|
||||
socket_path = default_socket_path,
|
||||
texthooker_enabled = true,
|
||||
texthooker_enabled = false,
|
||||
texthooker_port = 5174,
|
||||
backend = "auto",
|
||||
auto_start = true,
|
||||
auto_start_visible_overlay = true,
|
||||
auto_start = false,
|
||||
auto_start_visible_overlay = false,
|
||||
auto_start_pause_until_ready = true,
|
||||
auto_start_pause_until_ready_timeout_seconds = 15,
|
||||
osd_messages = true,
|
||||
log_level = "info",
|
||||
aniskip_enabled = true,
|
||||
aniskip_enabled = false,
|
||||
aniskip_title = "",
|
||||
aniskip_season = "",
|
||||
aniskip_mal_id = "",
|
||||
|
||||
+192
-42
@@ -2,12 +2,15 @@ local M = {}
|
||||
|
||||
local OVERLAY_START_RETRY_DELAY_SECONDS = 0.2
|
||||
local OVERLAY_START_MAX_ATTEMPTS = 6
|
||||
local OVERLAY_RESTART_PING_RETRY_DELAY_SECONDS = 0.2
|
||||
local OVERLAY_RESTART_PING_MAX_ATTEMPTS = 20
|
||||
local AUTO_PLAY_READY_LOADING_OSD = "Loading subtitle tokenization..."
|
||||
local AUTO_PLAY_READY_READY_OSD = "Subtitle tokenization ready"
|
||||
local DEFAULT_AUTO_PLAY_READY_TIMEOUT_SECONDS = 15
|
||||
|
||||
function M.create(ctx)
|
||||
local mp = ctx.mp
|
||||
local utils = ctx.utils
|
||||
local opts = ctx.opts
|
||||
local state = ctx.state
|
||||
local binary = ctx.binary
|
||||
@@ -17,6 +20,8 @@ function M.create(ctx)
|
||||
local show_osd = ctx.log.show_osd
|
||||
local normalize_log_level = ctx.log.normalize_log_level
|
||||
local run_control_command_async
|
||||
local APP_ARGC_ENV = "SUBMINER_APP_ARGC"
|
||||
local APP_ARG_PREFIX = "SUBMINER_APP_ARG_"
|
||||
|
||||
local function resolve_visible_overlay_startup()
|
||||
local raw_visible_overlay = opts.auto_start_visible_overlay
|
||||
@@ -112,10 +117,12 @@ function M.create(ctx)
|
||||
local function disarm_auto_play_ready_gate(options)
|
||||
local should_resume = options == nil or options.resume_playback ~= false
|
||||
local was_armed = state.auto_play_ready_gate_armed
|
||||
local should_resume_playback = state.auto_play_ready_should_resume_playback == true
|
||||
clear_auto_play_ready_timeout()
|
||||
clear_auto_play_ready_osd_timer()
|
||||
state.auto_play_ready_gate_armed = false
|
||||
if was_armed and should_resume then
|
||||
state.auto_play_ready_should_resume_playback = false
|
||||
if was_armed and should_resume and should_resume_playback then
|
||||
mp.set_property_native("pause", false)
|
||||
end
|
||||
end
|
||||
@@ -124,17 +131,26 @@ function M.create(ctx)
|
||||
if not state.auto_play_ready_gate_armed then
|
||||
return
|
||||
end
|
||||
local should_resume_playback = state.auto_play_ready_should_resume_playback == true
|
||||
disarm_auto_play_ready_gate({ resume_playback = false })
|
||||
mp.set_property_native("pause", false)
|
||||
show_osd(AUTO_PLAY_READY_READY_OSD)
|
||||
subminer_log("info", "process", "Resuming playback after startup gate: " .. tostring(reason or "ready"))
|
||||
if should_resume_playback then
|
||||
mp.set_property_native("pause", false)
|
||||
subminer_log("info", "process", "Resuming playback after startup gate: " .. tostring(reason or "ready"))
|
||||
else
|
||||
subminer_log("info", "process", "Startup gate ready; leaving playback paused: " .. tostring(reason or "ready"))
|
||||
end
|
||||
end
|
||||
|
||||
local function arm_auto_play_ready_gate()
|
||||
if state.auto_play_ready_gate_armed then
|
||||
local was_armed = state.auto_play_ready_gate_armed
|
||||
if was_armed then
|
||||
clear_auto_play_ready_timeout()
|
||||
clear_auto_play_ready_osd_timer()
|
||||
end
|
||||
if not was_armed then
|
||||
state.auto_play_ready_should_resume_playback = mp.get_property_native("pause") ~= true
|
||||
end
|
||||
state.auto_play_ready_gate_armed = true
|
||||
mp.set_property_native("pause", true)
|
||||
show_osd(AUTO_PLAY_READY_LOADING_OSD)
|
||||
@@ -164,10 +180,15 @@ function M.create(ctx)
|
||||
|
||||
local function notify_auto_play_ready()
|
||||
release_auto_play_ready_gate("tokenization-ready")
|
||||
if state.suppress_ready_overlay_restore then
|
||||
local force_ready_overlay_restore = state.force_ready_overlay_restore == true
|
||||
state.force_ready_overlay_restore = false
|
||||
if state.suppress_ready_overlay_restore and not force_ready_overlay_restore then
|
||||
return
|
||||
end
|
||||
if state.overlay_running and resolve_visible_overlay_startup() then
|
||||
if force_ready_overlay_restore then
|
||||
state.suppress_ready_overlay_restore = false
|
||||
end
|
||||
if state.overlay_running and (force_ready_overlay_restore or resolve_visible_overlay_startup()) then
|
||||
run_control_command_async("show-visible-overlay", {
|
||||
socket_path = opts.socket_path,
|
||||
})
|
||||
@@ -186,7 +207,9 @@ function M.create(ctx)
|
||||
end
|
||||
|
||||
if action == "start" then
|
||||
table.insert(args, "--background")
|
||||
if overrides.background ~= false then
|
||||
table.insert(args, "--background")
|
||||
end
|
||||
table.insert(args, "--managed-playback")
|
||||
|
||||
local backend = resolve_backend(overrides.backend)
|
||||
@@ -199,7 +222,10 @@ function M.create(ctx)
|
||||
table.insert(args, "--socket")
|
||||
table.insert(args, socket_path)
|
||||
|
||||
local should_show_visible = resolve_visible_overlay_startup()
|
||||
local should_show_visible = overrides.show_visible_overlay
|
||||
if should_show_visible == nil then
|
||||
should_show_visible = resolve_visible_overlay_startup()
|
||||
end
|
||||
if should_show_visible then
|
||||
table.insert(args, "--show-visible-overlay")
|
||||
else
|
||||
@@ -215,12 +241,75 @@ function M.create(ctx)
|
||||
return args
|
||||
end
|
||||
|
||||
local function is_appimage_binary(path)
|
||||
return environment.is_linux() and type(path) == "string" and path:lower():match("%.appimage$") ~= nil
|
||||
end
|
||||
|
||||
local function append_transport_env(env, args)
|
||||
local count = math.max(#args - 1, 0)
|
||||
env[#env + 1] = APP_ARGC_ENV .. "=" .. tostring(count)
|
||||
for index = 2, #args do
|
||||
env[#env + 1] = APP_ARG_PREFIX .. tostring(index - 2) .. "=" .. tostring(args[index])
|
||||
end
|
||||
end
|
||||
|
||||
local function env_has_name(env, name)
|
||||
local prefix = name .. "="
|
||||
for _, value in ipairs(env) do
|
||||
if type(value) == "string" and value:sub(1, #prefix) == prefix then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function append_default_app_log_env(env)
|
||||
local log_dir = environment.join_path(environment.resolve_subminer_config_dir(), "logs")
|
||||
local date = os.date("%Y-%m-%d")
|
||||
if not env_has_name(env, "SUBMINER_APP_LOG") then
|
||||
env[#env + 1] = "SUBMINER_APP_LOG=" .. environment.join_path(log_dir, "app-" .. date .. ".log")
|
||||
end
|
||||
if not env_has_name(env, "SUBMINER_MPV_LOG") then
|
||||
env[#env + 1] = "SUBMINER_MPV_LOG=" .. environment.join_path(log_dir, "mpv-" .. date .. ".log")
|
||||
end
|
||||
end
|
||||
|
||||
local function build_appimage_subprocess_env(args)
|
||||
local env = {}
|
||||
if utils and type(utils.get_env_list) == "function" then
|
||||
for _, value in ipairs(utils.get_env_list()) do
|
||||
if
|
||||
type(value) == "string"
|
||||
and not value:match("^" .. APP_ARGC_ENV .. "=")
|
||||
and not value:match("^" .. APP_ARG_PREFIX .. "%d+=")
|
||||
then
|
||||
env[#env + 1] = value
|
||||
end
|
||||
end
|
||||
end
|
||||
append_default_app_log_env(env)
|
||||
append_transport_env(env, args)
|
||||
return env
|
||||
end
|
||||
|
||||
local function build_subprocess_command(args)
|
||||
if is_appimage_binary(args[1]) then
|
||||
return {
|
||||
args = { args[1] },
|
||||
env = build_appimage_subprocess_env(args),
|
||||
}
|
||||
end
|
||||
return { args = args }
|
||||
end
|
||||
|
||||
run_control_command_async = function(action, overrides, callback)
|
||||
local args = build_command_args(action, overrides)
|
||||
local command = build_subprocess_command(args)
|
||||
subminer_log("debug", "process", "Control command: " .. table.concat(args, " "))
|
||||
mp.command_native_async({
|
||||
name = "subprocess",
|
||||
args = args,
|
||||
args = command.args,
|
||||
env = command.env,
|
||||
playback_only = false,
|
||||
capture_stdout = true,
|
||||
capture_stderr = true,
|
||||
@@ -232,11 +321,36 @@ function M.create(ctx)
|
||||
end)
|
||||
end
|
||||
|
||||
local function wait_for_app_ping_state(expected_running, label, on_ready, on_timeout, attempt)
|
||||
attempt = attempt or 1
|
||||
run_control_command_async("app-ping", nil, function(_ok, result)
|
||||
local status = result and result.status
|
||||
local is_running = status == 0
|
||||
local is_not_running = status == 1
|
||||
if (expected_running and is_running) or ((not expected_running) and is_not_running) then
|
||||
on_ready()
|
||||
return
|
||||
end
|
||||
if attempt >= OVERLAY_RESTART_PING_MAX_ATTEMPTS then
|
||||
subminer_log("warn", "process", "Timed out waiting for SubMiner app to " .. label)
|
||||
if on_timeout then
|
||||
on_timeout()
|
||||
end
|
||||
return
|
||||
end
|
||||
mp.add_timeout(OVERLAY_RESTART_PING_RETRY_DELAY_SECONDS, function()
|
||||
wait_for_app_ping_state(expected_running, label, on_ready, on_timeout, attempt + 1)
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
local function run_binary_command_async(args, callback)
|
||||
local command = build_subprocess_command(args)
|
||||
subminer_log("debug", "process", "Binary command: " .. table.concat(args, " "))
|
||||
mp.command_native_async({
|
||||
name = "subprocess",
|
||||
args = args,
|
||||
args = command.args,
|
||||
env = command.env,
|
||||
playback_only = false,
|
||||
capture_stdout = true,
|
||||
capture_stderr = true,
|
||||
@@ -299,7 +413,15 @@ function M.create(ctx)
|
||||
if overrides.auto_start_trigger == true then
|
||||
subminer_log("debug", "process", "Auto-start ignored because overlay is already running")
|
||||
local socket_path = overrides.socket_path or opts.socket_path
|
||||
if not state.auto_play_ready_gate_armed then
|
||||
local should_pause_until_ready = (
|
||||
overrides.rearm_pause_until_ready == 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()
|
||||
elseif not state.auto_play_ready_gate_armed then
|
||||
disarm_auto_play_ready_gate()
|
||||
end
|
||||
local visibility_action = resolve_visible_overlay_startup()
|
||||
@@ -347,9 +469,11 @@ function M.create(ctx)
|
||||
end
|
||||
state.overlay_running = true
|
||||
|
||||
local command = build_subprocess_command(args)
|
||||
mp.command_native_async({
|
||||
name = "subprocess",
|
||||
args = args,
|
||||
args = command.args,
|
||||
env = command.env,
|
||||
playback_only = false,
|
||||
capture_stdout = true,
|
||||
capture_stderr = true,
|
||||
@@ -383,10 +507,13 @@ function M.create(ctx)
|
||||
end)
|
||||
end
|
||||
|
||||
launch_overlay_with_retry(1)
|
||||
if texthooker_enabled then
|
||||
ensure_texthooker_running(function() end)
|
||||
end
|
||||
environment.is_subminer_app_running_async(function(app_running)
|
||||
overrides.background = not app_running
|
||||
launch_overlay_with_retry(1)
|
||||
if texthooker_enabled then
|
||||
ensure_texthooker_running(function() end)
|
||||
end
|
||||
end, { force_refresh = true })
|
||||
end
|
||||
|
||||
local function start_overlay_from_script_message(...)
|
||||
@@ -501,38 +628,61 @@ function M.create(ctx)
|
||||
subminer_log("info", "process", "Restarting overlay...")
|
||||
show_osd("Restarting...")
|
||||
|
||||
run_control_command_async("stop", nil, function()
|
||||
run_control_command_async("stop", nil, function(ok, result)
|
||||
if not ok then
|
||||
local reason = result and result.stderr or "unknown error"
|
||||
subminer_log("warn", "process", "Restart stop command failed: " .. reason)
|
||||
show_osd("Restart failed")
|
||||
return
|
||||
end
|
||||
|
||||
state.overlay_running = false
|
||||
state.texthooker_running = false
|
||||
disarm_auto_play_ready_gate()
|
||||
state.suppress_ready_overlay_restore = false
|
||||
state.force_ready_overlay_restore = true
|
||||
disarm_auto_play_ready_gate({ resume_playback = false })
|
||||
|
||||
local start_args = build_command_args("start")
|
||||
subminer_log("info", "process", "Starting overlay: " .. table.concat(start_args, " "))
|
||||
wait_for_app_ping_state(false, "release the single-instance lock", function()
|
||||
local start_args = build_command_args("start", {
|
||||
show_visible_overlay = true,
|
||||
})
|
||||
subminer_log("info", "process", "Starting overlay: " .. table.concat(start_args, " "))
|
||||
|
||||
state.overlay_running = true
|
||||
mp.command_native_async({
|
||||
name = "subprocess",
|
||||
args = start_args,
|
||||
playback_only = false,
|
||||
capture_stdout = true,
|
||||
capture_stderr = true,
|
||||
}, function(success, result, error)
|
||||
if not success or (result and result.status ~= 0) then
|
||||
state.overlay_running = false
|
||||
subminer_log(
|
||||
"error",
|
||||
"process",
|
||||
"Overlay start failed: " .. (error or (result and result.stderr) or "unknown error")
|
||||
)
|
||||
show_osd("Restart failed")
|
||||
else
|
||||
show_osd("Restarted successfully")
|
||||
state.overlay_running = true
|
||||
local command = build_subprocess_command(start_args)
|
||||
mp.command_native_async({
|
||||
name = "subprocess",
|
||||
args = command.args,
|
||||
env = command.env,
|
||||
playback_only = false,
|
||||
capture_stdout = true,
|
||||
capture_stderr = true,
|
||||
}, function(success, result, error)
|
||||
if not success or (result and result.status ~= 0) then
|
||||
state.overlay_running = false
|
||||
subminer_log(
|
||||
"error",
|
||||
"process",
|
||||
"Overlay start failed: " .. (error or (result and result.stderr) or "unknown error")
|
||||
)
|
||||
show_osd("Restart failed")
|
||||
else
|
||||
wait_for_app_ping_state(true, "own the single-instance lock", function()
|
||||
run_control_command_async("show-visible-overlay")
|
||||
show_osd("Restarted successfully")
|
||||
end, function()
|
||||
run_control_command_async("show-visible-overlay")
|
||||
show_osd("Restarted successfully")
|
||||
end)
|
||||
end
|
||||
end)
|
||||
|
||||
if resolve_texthooker_enabled(nil) then
|
||||
ensure_texthooker_running(function() end)
|
||||
end
|
||||
end, function()
|
||||
show_osd("Restart failed")
|
||||
end)
|
||||
|
||||
if resolve_texthooker_enabled(nil) then
|
||||
ensure_texthooker_running(function() end)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
@@ -151,6 +151,8 @@ function M.create(ctx)
|
||||
return { "--toggle-subtitle-sidebar" }
|
||||
elseif action_id == "markAudioCard" then
|
||||
return { "--mark-audio-card" }
|
||||
elseif action_id == "markWatched" then
|
||||
return { "--mark-watched" }
|
||||
elseif action_id == "openRuntimeOptions" then
|
||||
return { "--open-runtime-options" }
|
||||
elseif action_id == "openJimaku" then
|
||||
|
||||
@@ -30,11 +30,14 @@ function M.new()
|
||||
prompt_shown = false,
|
||||
},
|
||||
auto_play_ready_gate_armed = false,
|
||||
auto_play_ready_should_resume_playback = false,
|
||||
auto_play_ready_timeout = nil,
|
||||
auto_play_ready_osd_timer = nil,
|
||||
suppress_ready_overlay_restore = false,
|
||||
force_ready_overlay_restore = false,
|
||||
current_media_identity = nil,
|
||||
pending_reload_media_identity = nil,
|
||||
auto_start_retry_generation = 0,
|
||||
session_binding_generation = 0,
|
||||
session_binding_names = {},
|
||||
session_numeric_binding_names = {},
|
||||
|
||||
Reference in New Issue
Block a user