mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-03-30 06:12:06 -07:00
fix: stabilize macOS visible overlay toggle
This commit is contained in:
@@ -153,6 +153,9 @@ function M.create(ctx)
|
|||||||
|
|
||||||
local function notify_auto_play_ready()
|
local function notify_auto_play_ready()
|
||||||
release_auto_play_ready_gate("tokenization-ready")
|
release_auto_play_ready_gate("tokenization-ready")
|
||||||
|
if state.suppress_ready_overlay_restore then
|
||||||
|
return
|
||||||
|
end
|
||||||
if state.overlay_running and resolve_visible_overlay_startup() then
|
if state.overlay_running and resolve_visible_overlay_startup() then
|
||||||
run_control_command_async("show-visible-overlay", {
|
run_control_command_async("show-visible-overlay", {
|
||||||
socket_path = opts.socket_path,
|
socket_path = opts.socket_path,
|
||||||
@@ -287,6 +290,9 @@ function M.create(ctx)
|
|||||||
|
|
||||||
local function start_overlay(overrides)
|
local function start_overlay(overrides)
|
||||||
overrides = overrides or {}
|
overrides = overrides or {}
|
||||||
|
if overrides.auto_start_trigger == true then
|
||||||
|
state.suppress_ready_overlay_restore = false
|
||||||
|
end
|
||||||
|
|
||||||
if not binary.ensure_binary_available() then
|
if not binary.ensure_binary_available() then
|
||||||
subminer_log("error", "binary", "SubMiner binary not found")
|
subminer_log("error", "binary", "SubMiner binary not found")
|
||||||
@@ -433,6 +439,7 @@ function M.create(ctx)
|
|||||||
subminer_log("error", "binary", "SubMiner binary not found")
|
subminer_log("error", "binary", "SubMiner binary not found")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
state.suppress_ready_overlay_restore = true
|
||||||
|
|
||||||
run_control_command_async("hide-visible-overlay", nil, function(ok, result)
|
run_control_command_async("hide-visible-overlay", nil, function(ok, result)
|
||||||
if ok then
|
if ok then
|
||||||
@@ -456,8 +463,9 @@ function M.create(ctx)
|
|||||||
show_osd("Error: binary not found")
|
show_osd("Error: binary not found")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
state.suppress_ready_overlay_restore = true
|
||||||
|
|
||||||
run_control_command_async("toggle", nil, function(ok)
|
run_control_command_async("toggle-visible-overlay", nil, function(ok)
|
||||||
if not ok then
|
if not ok then
|
||||||
subminer_log("warn", "process", "Toggle command failed")
|
subminer_log("warn", "process", "Toggle command failed")
|
||||||
show_osd("Toggle failed")
|
show_osd("Toggle failed")
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ function M.new()
|
|||||||
auto_play_ready_gate_armed = false,
|
auto_play_ready_gate_armed = false,
|
||||||
auto_play_ready_timeout = nil,
|
auto_play_ready_timeout = nil,
|
||||||
auto_play_ready_osd_timer = nil,
|
auto_play_ready_osd_timer = nil,
|
||||||
|
suppress_ready_overlay_restore = false,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -822,6 +822,92 @@ do
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
do
|
||||||
|
local recorded, err = run_plugin_scenario({
|
||||||
|
process_list = "",
|
||||||
|
option_overrides = {
|
||||||
|
binary_path = binary_path,
|
||||||
|
auto_start = "yes",
|
||||||
|
auto_start_visible_overlay = "yes",
|
||||||
|
auto_start_pause_until_ready = "yes",
|
||||||
|
socket_path = "/tmp/subminer-socket",
|
||||||
|
},
|
||||||
|
input_ipc_server = "/tmp/subminer-socket",
|
||||||
|
media_title = "Random Movie",
|
||||||
|
files = {
|
||||||
|
[binary_path] = true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
assert_true(recorded ~= nil, "plugin failed to load for manual toggle-off ready scenario: " .. tostring(err))
|
||||||
|
fire_event(recorded, "file-loaded")
|
||||||
|
assert_true(recorded.script_messages["subminer-toggle"] ~= nil, "subminer-toggle script message not registered")
|
||||||
|
recorded.script_messages["subminer-toggle"]()
|
||||||
|
assert_true(
|
||||||
|
count_control_calls(recorded.async_calls, "--toggle-visible-overlay") == 1,
|
||||||
|
"manual toggle should use explicit visible-overlay toggle command"
|
||||||
|
)
|
||||||
|
recorded.script_messages["subminer-autoplay-ready"]()
|
||||||
|
assert_true(
|
||||||
|
count_control_calls(recorded.async_calls, "--show-visible-overlay") == 1,
|
||||||
|
"manual toggle-off before readiness should suppress ready-time visible overlay restore"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
do
|
||||||
|
local recorded, err = run_plugin_scenario({
|
||||||
|
process_list = "",
|
||||||
|
option_overrides = {
|
||||||
|
binary_path = binary_path,
|
||||||
|
auto_start = "yes",
|
||||||
|
auto_start_visible_overlay = "yes",
|
||||||
|
auto_start_pause_until_ready = "yes",
|
||||||
|
socket_path = "/tmp/subminer-socket",
|
||||||
|
},
|
||||||
|
input_ipc_server = "/tmp/subminer-socket",
|
||||||
|
media_title = "Random Movie",
|
||||||
|
files = {
|
||||||
|
[binary_path] = true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
assert_true(
|
||||||
|
recorded ~= nil,
|
||||||
|
"plugin failed to load for repeated ready restore suppression scenario: " .. tostring(err)
|
||||||
|
)
|
||||||
|
fire_event(recorded, "file-loaded")
|
||||||
|
assert_true(recorded.script_messages["subminer-toggle"] ~= nil, "subminer-toggle script message not registered")
|
||||||
|
recorded.script_messages["subminer-toggle"]()
|
||||||
|
recorded.script_messages["subminer-autoplay-ready"]()
|
||||||
|
recorded.script_messages["subminer-autoplay-ready"]()
|
||||||
|
assert_true(
|
||||||
|
count_control_calls(recorded.async_calls, "--show-visible-overlay") == 1,
|
||||||
|
"manual toggle-off should suppress repeated ready-time visible overlay restores for the same session"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
do
|
||||||
|
local recorded, err = run_plugin_scenario({
|
||||||
|
process_list = "",
|
||||||
|
option_overrides = {
|
||||||
|
binary_path = binary_path,
|
||||||
|
auto_start = "no",
|
||||||
|
},
|
||||||
|
files = {
|
||||||
|
[binary_path] = true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
assert_true(recorded ~= nil, "plugin failed to load for manual toggle command scenario: " .. tostring(err))
|
||||||
|
assert_true(recorded.script_messages["subminer-toggle"] ~= nil, "subminer-toggle script message not registered")
|
||||||
|
recorded.script_messages["subminer-toggle"]()
|
||||||
|
assert_true(
|
||||||
|
count_control_calls(recorded.async_calls, "--toggle-visible-overlay") == 1,
|
||||||
|
"script-message toggle should issue explicit visible-overlay toggle command"
|
||||||
|
)
|
||||||
|
assert_true(
|
||||||
|
count_control_calls(recorded.async_calls, "--toggle") == 0,
|
||||||
|
"script-message toggle should not issue legacy generic toggle command"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
do
|
do
|
||||||
local recorded, err = run_plugin_scenario({
|
local recorded, err = run_plugin_scenario({
|
||||||
process_list = "",
|
process_list = "",
|
||||||
|
|||||||
@@ -443,13 +443,23 @@ test('handleCliCommand still runs non-start actions on second-instance', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('handleCliCommand connects MPV for toggle on second-instance', () => {
|
test('handleCliCommand does not connect MPV for pure toggle on second-instance', () => {
|
||||||
const { deps, calls } = createDeps();
|
const { deps, calls } = createDeps();
|
||||||
handleCliCommand(makeArgs({ toggle: true }), 'second-instance', deps);
|
handleCliCommand(makeArgs({ toggle: true }), 'second-instance', deps);
|
||||||
assert.ok(calls.includes('toggleVisibleOverlay'));
|
assert.ok(calls.includes('toggleVisibleOverlay'));
|
||||||
assert.equal(
|
assert.equal(
|
||||||
calls.some((value) => value === 'connectMpvClient'),
|
calls.some((value) => value === 'connectMpvClient'),
|
||||||
true,
|
false,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('handleCliCommand does not connect MPV for explicit visible-overlay toggle', () => {
|
||||||
|
const { deps, calls } = createDeps();
|
||||||
|
handleCliCommand(makeArgs({ toggleVisibleOverlay: true }), 'second-instance', deps);
|
||||||
|
assert.ok(calls.includes('toggleVisibleOverlay'));
|
||||||
|
assert.equal(
|
||||||
|
calls.some((value) => value === 'connectMpvClient'),
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -271,7 +271,7 @@ export function handleCliCommand(
|
|||||||
|
|
||||||
const reuseSecondInstanceStart =
|
const reuseSecondInstanceStart =
|
||||||
source === 'second-instance' && args.start && deps.isOverlayRuntimeInitialized();
|
source === 'second-instance' && args.start && deps.isOverlayRuntimeInitialized();
|
||||||
const shouldStart = args.start || args.toggle || args.toggleVisibleOverlay;
|
const shouldConnectMpv = args.start;
|
||||||
const needsOverlayRuntime = commandNeedsOverlayRuntime(args);
|
const needsOverlayRuntime = commandNeedsOverlayRuntime(args);
|
||||||
const shouldInitializeOverlayRuntime = needsOverlayRuntime || args.start;
|
const shouldInitializeOverlayRuntime = needsOverlayRuntime || args.start;
|
||||||
|
|
||||||
@@ -302,7 +302,7 @@ export function handleCliCommand(
|
|||||||
deps.initializeOverlayRuntime();
|
deps.initializeOverlayRuntime();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shouldStart && deps.hasMpvClient()) {
|
if (shouldConnectMpv && deps.hasMpvClient()) {
|
||||||
const socketPath = deps.getMpvSocketPath();
|
const socketPath = deps.getMpvSocketPath();
|
||||||
deps.setMpvClientSocketPath(socketPath);
|
deps.setMpvClientSocketPath(socketPath);
|
||||||
deps.connectMpvClient();
|
deps.connectMpvClient();
|
||||||
|
|||||||
@@ -59,3 +59,21 @@ export function handleOverlayWindowBeforeInputEvent(options: {
|
|||||||
options.preventDefault();
|
options.preventDefault();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function handleOverlayWindowBlurred(options: {
|
||||||
|
kind: OverlayWindowKind;
|
||||||
|
windowVisible: boolean;
|
||||||
|
isOverlayVisible: (kind: OverlayWindowKind) => boolean;
|
||||||
|
ensureOverlayWindowLevel: () => void;
|
||||||
|
moveWindowTop: () => void;
|
||||||
|
}): boolean {
|
||||||
|
if (options.kind === 'visible' && !options.isOverlayVisible(options.kind)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
options.ensureOverlayWindowLevel();
|
||||||
|
if (options.kind === 'visible' && options.windowVisible) {
|
||||||
|
options.moveWindowTop();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import assert from 'node:assert/strict';
|
|||||||
import test from 'node:test';
|
import test from 'node:test';
|
||||||
import {
|
import {
|
||||||
handleOverlayWindowBeforeInputEvent,
|
handleOverlayWindowBeforeInputEvent,
|
||||||
|
handleOverlayWindowBlurred,
|
||||||
isTabInputForMpvForwarding,
|
isTabInputForMpvForwarding,
|
||||||
} from './overlay-window-input';
|
} from './overlay-window-input';
|
||||||
|
|
||||||
@@ -82,3 +83,58 @@ test('handleOverlayWindowBeforeInputEvent leaves modal Tab handling alone', () =
|
|||||||
assert.equal(handled, false);
|
assert.equal(handled, false);
|
||||||
assert.deepEqual(calls, []);
|
assert.deepEqual(calls, []);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('handleOverlayWindowBlurred skips visible overlay restacking after manual hide', () => {
|
||||||
|
const calls: string[] = [];
|
||||||
|
|
||||||
|
const handled = handleOverlayWindowBlurred({
|
||||||
|
kind: 'visible',
|
||||||
|
windowVisible: true,
|
||||||
|
isOverlayVisible: () => false,
|
||||||
|
ensureOverlayWindowLevel: () => {
|
||||||
|
calls.push('ensure-level');
|
||||||
|
},
|
||||||
|
moveWindowTop: () => {
|
||||||
|
calls.push('move-top');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(handled, false);
|
||||||
|
assert.deepEqual(calls, []);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('handleOverlayWindowBlurred preserves active visible/modal window stacking', () => {
|
||||||
|
const calls: string[] = [];
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
handleOverlayWindowBlurred({
|
||||||
|
kind: 'visible',
|
||||||
|
windowVisible: true,
|
||||||
|
isOverlayVisible: () => true,
|
||||||
|
ensureOverlayWindowLevel: () => {
|
||||||
|
calls.push('ensure-visible');
|
||||||
|
},
|
||||||
|
moveWindowTop: () => {
|
||||||
|
calls.push('move-visible');
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
handleOverlayWindowBlurred({
|
||||||
|
kind: 'modal',
|
||||||
|
windowVisible: true,
|
||||||
|
isOverlayVisible: () => false,
|
||||||
|
ensureOverlayWindowLevel: () => {
|
||||||
|
calls.push('ensure-modal');
|
||||||
|
},
|
||||||
|
moveWindowTop: () => {
|
||||||
|
calls.push('move-modal');
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.deepEqual(calls, ['ensure-visible', 'move-visible', 'ensure-modal']);
|
||||||
|
});
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { createLogger } from '../../logger';
|
|||||||
import { IPC_CHANNELS } from '../../shared/ipc/contracts';
|
import { IPC_CHANNELS } from '../../shared/ipc/contracts';
|
||||||
import {
|
import {
|
||||||
handleOverlayWindowBeforeInputEvent,
|
handleOverlayWindowBeforeInputEvent,
|
||||||
|
handleOverlayWindowBlurred,
|
||||||
type OverlayWindowKind,
|
type OverlayWindowKind,
|
||||||
} from './overlay-window-input';
|
} from './overlay-window-input';
|
||||||
import { buildOverlayWindowOptions } from './overlay-window-options';
|
import { buildOverlayWindowOptions } from './overlay-window-options';
|
||||||
@@ -124,12 +125,18 @@ export function createOverlayWindow(
|
|||||||
});
|
});
|
||||||
|
|
||||||
window.on('blur', () => {
|
window.on('blur', () => {
|
||||||
if (!window.isDestroyed()) {
|
if (window.isDestroyed()) return;
|
||||||
|
handleOverlayWindowBlurred({
|
||||||
|
kind,
|
||||||
|
windowVisible: window.isVisible(),
|
||||||
|
isOverlayVisible: options.isOverlayVisible,
|
||||||
|
ensureOverlayWindowLevel: () => {
|
||||||
options.ensureOverlayWindowLevel(window);
|
options.ensureOverlayWindowLevel(window);
|
||||||
if (kind === 'visible' && window.isVisible()) {
|
},
|
||||||
|
moveWindowTop: () => {
|
||||||
window.moveTop();
|
window.moveTop();
|
||||||
}
|
},
|
||||||
}
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
if (options.isDev && kind === 'visible') {
|
if (options.isDev && kind === 'visible') {
|
||||||
|
|||||||
@@ -33,13 +33,66 @@ test('autoplay ready gate suppresses duplicate media signals unless forced while
|
|||||||
gate.maybeSignalPluginAutoplayReady({ text: '字幕', tokens: null });
|
gate.maybeSignalPluginAutoplayReady({ text: '字幕', tokens: null });
|
||||||
gate.maybeSignalPluginAutoplayReady({ text: '字幕', tokens: null }, { forceWhilePaused: true });
|
gate.maybeSignalPluginAutoplayReady({ text: '字幕', tokens: null }, { forceWhilePaused: true });
|
||||||
|
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||||
|
const firstScheduled = scheduled.shift();
|
||||||
|
firstScheduled?.();
|
||||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||||
|
|
||||||
assert.deepEqual(commands.slice(0, 3), [
|
assert.deepEqual(commands.filter((command) => command[0] === 'script-message'), [
|
||||||
['script-message', 'subminer-autoplay-ready'],
|
|
||||||
['script-message', 'subminer-autoplay-ready'],
|
|
||||||
['script-message', 'subminer-autoplay-ready'],
|
['script-message', 'subminer-autoplay-ready'],
|
||||||
]);
|
]);
|
||||||
assert.ok(commands.some((command) => command[0] === 'set_property' && command[1] === 'pause'));
|
assert.ok(
|
||||||
|
commands.some(
|
||||||
|
(command) =>
|
||||||
|
command[0] === 'set_property' && command[1] === 'pause' && command[2] === false,
|
||||||
|
),
|
||||||
|
);
|
||||||
assert.equal(scheduled.length > 0, true);
|
assert.equal(scheduled.length > 0, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('autoplay ready gate retry loop does not re-signal plugin readiness', async () => {
|
||||||
|
const commands: Array<Array<string | boolean>> = [];
|
||||||
|
const scheduled: Array<() => void> = [];
|
||||||
|
|
||||||
|
const gate = createAutoplayReadyGate({
|
||||||
|
isAppOwnedFlowInFlight: () => false,
|
||||||
|
getCurrentMediaPath: () => '/media/video.mkv',
|
||||||
|
getCurrentVideoPath: () => null,
|
||||||
|
getPlaybackPaused: () => true,
|
||||||
|
getMpvClient: () =>
|
||||||
|
({
|
||||||
|
connected: true,
|
||||||
|
requestProperty: async () => true,
|
||||||
|
send: ({ command }: { command: Array<string | boolean> }) => {
|
||||||
|
commands.push(command);
|
||||||
|
},
|
||||||
|
}) as never,
|
||||||
|
signalPluginAutoplayReady: () => {
|
||||||
|
commands.push(['script-message', 'subminer-autoplay-ready']);
|
||||||
|
},
|
||||||
|
schedule: (callback) => {
|
||||||
|
scheduled.push(callback);
|
||||||
|
return 1 as never;
|
||||||
|
},
|
||||||
|
logDebug: () => {},
|
||||||
|
});
|
||||||
|
|
||||||
|
gate.maybeSignalPluginAutoplayReady({ text: '字幕', tokens: null }, { forceWhilePaused: true });
|
||||||
|
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||||
|
for (const callback of scheduled.splice(0, 3)) {
|
||||||
|
callback();
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.deepEqual(commands.filter((command) => command[0] === 'script-message'), [
|
||||||
|
['script-message', 'subminer-autoplay-ready'],
|
||||||
|
]);
|
||||||
|
assert.equal(
|
||||||
|
commands.filter(
|
||||||
|
(command) =>
|
||||||
|
command[0] === 'set_property' && command[1] === 'pause' && command[2] === false,
|
||||||
|
).length > 0,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|||||||
@@ -46,19 +46,6 @@ export function createAutoplayReadyGate(deps: AutoplayReadyGateDeps) {
|
|||||||
const duplicateMediaSignal = autoPlayReadySignalMediaPath === mediaPath;
|
const duplicateMediaSignal = autoPlayReadySignalMediaPath === mediaPath;
|
||||||
const allowDuplicateWhilePaused =
|
const allowDuplicateWhilePaused =
|
||||||
options?.forceWhilePaused === true && deps.getPlaybackPaused() !== false;
|
options?.forceWhilePaused === true && deps.getPlaybackPaused() !== false;
|
||||||
if (duplicateMediaSignal && !allowDuplicateWhilePaused) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (duplicateMediaSignal && allowDuplicateWhilePaused) {
|
|
||||||
deps.signalPluginAutoplayReady();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
autoPlayReadySignalMediaPath = mediaPath;
|
|
||||||
const playbackGeneration = ++autoPlayReadySignalGeneration;
|
|
||||||
deps.signalPluginAutoplayReady();
|
|
||||||
|
|
||||||
const releaseRetryDelayMs = 200;
|
const releaseRetryDelayMs = 200;
|
||||||
const maxReleaseAttempts = resolveAutoplayReadyMaxReleaseAttempts({
|
const maxReleaseAttempts = resolveAutoplayReadyMaxReleaseAttempts({
|
||||||
forceWhilePaused: options?.forceWhilePaused === true,
|
forceWhilePaused: options?.forceWhilePaused === true,
|
||||||
@@ -88,7 +75,7 @@ export function createAutoplayReadyGate(deps: AutoplayReadyGateDeps) {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const attemptRelease = (attempt: number): void => {
|
const attemptRelease = (playbackGeneration: number, attempt: number): void => {
|
||||||
void (async () => {
|
void (async () => {
|
||||||
if (
|
if (
|
||||||
autoPlayReadySignalMediaPath !== mediaPath ||
|
autoPlayReadySignalMediaPath !== mediaPath ||
|
||||||
@@ -100,7 +87,7 @@ export function createAutoplayReadyGate(deps: AutoplayReadyGateDeps) {
|
|||||||
const mpvClient = deps.getMpvClient();
|
const mpvClient = deps.getMpvClient();
|
||||||
if (!mpvClient?.connected) {
|
if (!mpvClient?.connected) {
|
||||||
if (attempt < maxReleaseAttempts) {
|
if (attempt < maxReleaseAttempts) {
|
||||||
deps.schedule(() => attemptRelease(attempt + 1), releaseRetryDelayMs);
|
deps.schedule(() => attemptRelease(playbackGeneration, attempt + 1), releaseRetryDelayMs);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -110,15 +97,27 @@ export function createAutoplayReadyGate(deps: AutoplayReadyGateDeps) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
deps.signalPluginAutoplayReady();
|
|
||||||
mpvClient.send({ command: ['set_property', 'pause', false] });
|
mpvClient.send({ command: ['set_property', 'pause', false] });
|
||||||
if (attempt < maxReleaseAttempts) {
|
if (attempt < maxReleaseAttempts) {
|
||||||
deps.schedule(() => attemptRelease(attempt + 1), releaseRetryDelayMs);
|
deps.schedule(() => attemptRelease(playbackGeneration, attempt + 1), releaseRetryDelayMs);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
};
|
};
|
||||||
|
|
||||||
attemptRelease(0);
|
if (duplicateMediaSignal && !allowDuplicateWhilePaused) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!duplicateMediaSignal) {
|
||||||
|
autoPlayReadySignalMediaPath = mediaPath;
|
||||||
|
const playbackGeneration = ++autoPlayReadySignalGeneration;
|
||||||
|
deps.signalPluginAutoplayReady();
|
||||||
|
attemptRelease(playbackGeneration, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const playbackGeneration = ++autoPlayReadySignalGeneration;
|
||||||
|
attemptRelease(playbackGeneration, 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
Reference in New Issue
Block a user