mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-05-26 12:55:16 -07:00
b1bdeabca8
* fix(jellyfin): show overlay, inject plugin, and fix stats title on playb - Show visible overlay automatically during Jellyfin playback so subtitleStyle applies - Inject bundled mpv plugin on auto-launch so keybindings work without overlay focus - Group Jellyfin playback stats under item metadata (jellyfin://host/item/id) instead of stream URLs so episodes merge with matching local titles - Mark ffsubsync unavailable in subsync modal for remote media paths - Drain queued second-instance commands even when onReady throws * fix(overlay): stabilize macOS focus handoff and sidebar Yomitan pause - Keep overlay visible during macOS foreground probe after overlay blur - Hold sidebar hover-pause while a Yomitan lookup popup remains open * fix(jellyfin): fix discovery loop, device identity, tray state, and Disc - Derive device identity from OS hostname; remove legacy configurable client/device fields - Prevent discovery playback from reloading active item, misreporting pause state, and duplicate overlay restores - Restart stale tray discovery sessions without re-login when server drops SubMiner cast target - Sync tray discovery checkbox state on Linux after CLI/startup/remote-session changes - Stop Discord presence falling back to stream URLs; prime title before tokenized stream loads - Fix picker library discovery when log level is above info - Fix config.example.jsonc trailing commas and array formatting * docs(release): trim and consolidate prerelease notes for 0.15.0 - Remove breaking changes section and several redundant bullet points - Consolidate per-platform updater notes into a single entry - Normalize em-dash separators to hyphens in section headers * fix(config): remove trailing commas from config.example.jsonc - Strip trailing commas throughout both config.example.jsonc copies - Reformat inline arrays to multi-line for JSON strictness - Update Jellyfin subtitle preload and playback launch tests and impl * fix(tokenizer): preserve known-word highlight when POS filters suppress - Known-word cache matches now set isKnown=true even for tokens excluded by POS filters - POS exclusion gate suppresses N+1, frequency, and JLPT only; known status is computed before the gate - Jellyfin subtitle preload continues after cleanup failures instead of aborting - Update config docs and option description to document the known-word bypass behavior * fix(jellyfin): send explicit hide/show overlay instead of toggle - Track overlay visibility in plugin state; y-t uses explicit hide/show commands when state is known - Prevent paused Jellyfin playback from resuming on overlay hide - Fix subtitle cache cleanup to only remove dirs after successful cleanup * fix(jellyfin): fix remote progress sync, seek reporting, and startup sto - arm active playback before loadfile with loadedMediaPath: null to suppress premature stop events - force immediate progress report on seek-like position jumps at the mpv time-pos level - send positionTicks and failed=false in reportStopped payload - remove EventName from HTTP timeline payloads (websocket-only field) - add startup grace window to drop stop events before media finishes loading * fix(jellyfin): fix overlay toggle sync, redirect reload, and AppImage bi - Sync visible-overlay state back to plugin via script messages to avoid toggle/hide drift - Collapse duplicate toggle events within 250ms to prevent hide-then-show on single keypress - Preserve manual hide across Jellyfin path-changing redirects even when media-title drops - Rearm managed subtitle defaults on path-changing redirects - Route toggleVisibleOverlay session binding through plugin toggle instead of app-side IPC - Show Linux/Hyprland overlay passively (showInactive) to avoid stealing mpv keyboard focus - Fix AppImage binary resolution to prefer $APPIMAGE env over mounted inner binary - Add stats window layer management so delete/update dialogs appear above stats window - Fix Jellyfin remote progress sync during Linux websocket reconnect windows * Fix CodeRabbit review feedback * fix(jellyfin): subtitle timing, resume progress, and overlay sync - Add per-stream subtitle delay persistence and auto timeline-offset correction - Strip server-selected subtitle stream from mpv load URL; suppress plugin subtitle rearm and auto-start during app-managed preload - Fix resume position lost when mpv resets on stop; use last known position for final progress/stopped reports - Keep Play vs Resume distinct to avoid early seek race on normal play - Fix discovery resume when remote play sends StartPositionTicks=0 despite saved progress - Deduplicate show/hide overlay commands using recorded visibility state - Rewrite docs-site Jellyfin page around cast-to-device UX * test: update lifecycle cleanup assertion * fix: clear aborted playback state, fix overlay passthrough, and guard du - Reset app_managed_playback_pending on lifecycle cleanup to prevent state leak into next item - Record visible overlay action only after command succeeds, not before - Non-native passive overlay now always click-through on re-show (fix isNonNativePassiveOverlay ordering) - Defer activeParsedSubtitleMediaPath assignment until after prefetch completes - Move autoplay gate release into the hide branch of toggleVisibleOverlay - Clear active Jellyfin playback when stopping media that never loaded - Reset managed subtitle delay and delay key when no external tracks are available - Await async removeDir in subtitle cache cleanup - Guard duplicate delete clicks in MediaDetailView and SessionsTab with refs - Escape key in DeleteConfirmDialog now calls stopPropagation and stopImmediatePropagation
210 lines
4.8 KiB
Lua
210 lines
4.8 KiB
Lua
local function assert_true(condition, message)
|
|
if condition then
|
|
return
|
|
end
|
|
error(message)
|
|
end
|
|
|
|
local function has_flag(call, flag)
|
|
local args = call.args or {}
|
|
for _, arg in ipairs(args) do
|
|
if arg == flag then
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
local function has_timeout(timeouts, target)
|
|
for _, value in ipairs(timeouts) do
|
|
if math.abs(value - target) < 0.0001 then
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
local recorded = {
|
|
async_calls = {},
|
|
timeouts = {},
|
|
logs = {},
|
|
}
|
|
|
|
local start_attempts = 0
|
|
|
|
local mp = {}
|
|
|
|
function mp.command_native_async(command, callback)
|
|
recorded.async_calls[#recorded.async_calls + 1] = command
|
|
|
|
local success = true
|
|
local result = { status = 0, stdout = "", stderr = "" }
|
|
local err = nil
|
|
|
|
if has_flag(command, "--start") then
|
|
start_attempts = start_attempts + 1
|
|
if start_attempts == 1 then
|
|
success = false
|
|
result = { status = 1, stdout = "", stderr = "startup-not-ready" }
|
|
err = "startup-not-ready"
|
|
end
|
|
end
|
|
|
|
if callback then
|
|
callback(success, result, err)
|
|
end
|
|
end
|
|
|
|
function mp.add_timeout(seconds, callback)
|
|
recorded.timeouts[#recorded.timeouts + 1] = seconds
|
|
if callback then
|
|
callback()
|
|
end
|
|
end
|
|
|
|
local process_module = dofile("plugin/subminer/process.lua")
|
|
local process = process_module.create({
|
|
mp = mp,
|
|
opts = {
|
|
backend = "x11",
|
|
socket_path = "/tmp/subminer.sock",
|
|
log_level = "debug",
|
|
texthooker_enabled = true,
|
|
texthooker_port = 5174,
|
|
auto_start_visible_overlay = false,
|
|
},
|
|
state = {
|
|
binary_path = "/tmp/subminer",
|
|
overlay_running = false,
|
|
texthooker_running = false,
|
|
},
|
|
binary = {
|
|
ensure_binary_available = function()
|
|
return true
|
|
end,
|
|
},
|
|
environment = {
|
|
detect_backend = function()
|
|
return "x11"
|
|
end,
|
|
is_linux = function()
|
|
return false
|
|
end,
|
|
is_subminer_app_running_async = function(callback)
|
|
callback(false)
|
|
end,
|
|
},
|
|
options_helper = {
|
|
coerce_bool = function(value, default_value)
|
|
if value == true or value == "yes" or value == "true" then
|
|
return true
|
|
end
|
|
if value == false or value == "no" or value == "false" then
|
|
return false
|
|
end
|
|
return default_value
|
|
end,
|
|
},
|
|
log = {
|
|
subminer_log = function(_level, _scope, line)
|
|
recorded.logs[#recorded.logs + 1] = line
|
|
end,
|
|
show_osd = function(_) end,
|
|
normalize_log_level = function(value)
|
|
return value or "info"
|
|
end,
|
|
},
|
|
})
|
|
|
|
process.start_overlay()
|
|
|
|
assert_true(start_attempts == 2, "expected start overlay command retry after readiness failure")
|
|
assert_true(not has_timeout(recorded.timeouts, 0.35), "fixed texthooker wait (0.35s) should be removed")
|
|
assert_true(not has_timeout(recorded.timeouts, 0.6), "fixed startup visibility delay (0.6s) should be removed")
|
|
|
|
local retry_timeout_seen = false
|
|
for _, timeout_seconds in ipairs(recorded.timeouts) do
|
|
if timeout_seconds > 0 and timeout_seconds <= 0.25 then
|
|
retry_timeout_seen = true
|
|
break
|
|
end
|
|
end
|
|
assert_true(retry_timeout_seen, "expected shorter bounded retry timeout")
|
|
|
|
do
|
|
local visibility_state = {
|
|
binary_path = "/tmp/subminer",
|
|
overlay_running = true,
|
|
texthooker_running = false,
|
|
visible_overlay_requested = false,
|
|
}
|
|
local visibility_calls = {}
|
|
local visibility_mp = {}
|
|
|
|
function visibility_mp.command_native_async(command, callback)
|
|
visibility_calls[#visibility_calls + 1] = command
|
|
if callback then
|
|
callback(false, { status = 1, stdout = "", stderr = "failed" }, "failed")
|
|
end
|
|
end
|
|
|
|
local visibility_process = process_module.create({
|
|
mp = visibility_mp,
|
|
opts = {
|
|
backend = "x11",
|
|
socket_path = "/tmp/subminer.sock",
|
|
log_level = "debug",
|
|
texthooker_enabled = true,
|
|
texthooker_port = 5174,
|
|
auto_start_visible_overlay = false,
|
|
},
|
|
state = visibility_state,
|
|
binary = {
|
|
ensure_binary_available = function()
|
|
return true
|
|
end,
|
|
},
|
|
environment = {
|
|
detect_backend = function()
|
|
return "x11"
|
|
end,
|
|
is_linux = function()
|
|
return false
|
|
end,
|
|
is_subminer_app_running_async = function(callback)
|
|
callback(true)
|
|
end,
|
|
},
|
|
options_helper = {
|
|
coerce_bool = function(value, default_value)
|
|
if value == true or value == "yes" or value == "true" then
|
|
return true
|
|
end
|
|
if value == false or value == "no" or value == "false" then
|
|
return false
|
|
end
|
|
return default_value
|
|
end,
|
|
},
|
|
log = {
|
|
subminer_log = function(_level, _scope, line)
|
|
recorded.logs[#recorded.logs + 1] = line
|
|
end,
|
|
show_osd = function(_) end,
|
|
normalize_log_level = function(value)
|
|
return value or "info"
|
|
end,
|
|
},
|
|
})
|
|
|
|
visibility_process.run_control_command_async("show-visible-overlay")
|
|
|
|
assert_true(#visibility_calls == 1, "expected visible overlay command to run")
|
|
assert_true(
|
|
visibility_state.visible_overlay_requested == false,
|
|
"failed visible-overlay command should not update requested visibility state"
|
|
)
|
|
end
|
|
|
|
print("plugin process retry regression tests: OK")
|