mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-08 17:06:15 -08:00
fix(electron): wait for the login shell PATH only before the mpv probe
Embedded MPV support and prepare waited on every Linux call, although getSupport() returns before the bare-name `mpv --version` probe for the frame-copy engine, native Wayland, a disabled feature or a cached result. willProbeLinuxMpvExecutable() now gates the wait on the probe actually running. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
dcae8547e3
commit
ea7d6f6700
4 files changed
+98
-35
No files matched your search
@@ -6,7 +6,9 @@ jest.mock('electron', () => ({
|
||||
|
||||
const mockEmbeddedMpvService = {
|
||||
createSession: jest.fn(),
|
||||
prepareAddon: jest.fn(),
|
||||
getSupport: jest.fn(),
|
||||
willProbeLinuxMpvExecutable: jest.fn(() => false),
|
||||
setPaused: jest.fn(),
|
||||
};
|
||||
const mockSessionOptions = {
|
||||
@@ -29,6 +31,7 @@ jest.mock('../startup/login-shell-path', () => ({
|
||||
import { ipcMain } from 'electron';
|
||||
import {
|
||||
EMBEDDED_MPV_CREATE_SESSION,
|
||||
EMBEDDED_MPV_PREPARE,
|
||||
EMBEDDED_MPV_SET_PAUSED,
|
||||
EMBEDDED_MPV_SUPPORT,
|
||||
} from '@iptvnator/shared/interfaces';
|
||||
@@ -60,45 +63,55 @@ describe('EmbeddedMpvEvents IPC handlers', () => {
|
||||
});
|
||||
|
||||
describe('support checks and the login shell PATH', () => {
|
||||
const originalPlatform = process.platform;
|
||||
|
||||
afterEach(() => {
|
||||
Object.defineProperty(process, 'platform', {
|
||||
value: originalPlatform,
|
||||
});
|
||||
mockWaitForLoginShellPath.mockClear();
|
||||
});
|
||||
|
||||
it('on Linux, checks support only after the lookup settled', async () => {
|
||||
Object.defineProperty(process, 'platform', { value: 'linux' });
|
||||
let settle: () => void = () => undefined;
|
||||
mockWaitForLoginShellPath.mockReturnValueOnce(
|
||||
new Promise<void>((resolve) => {
|
||||
settle = resolve;
|
||||
})
|
||||
mockEmbeddedMpvService.willProbeLinuxMpvExecutable.mockReset();
|
||||
mockEmbeddedMpvService.willProbeLinuxMpvExecutable.mockReturnValue(
|
||||
false
|
||||
);
|
||||
mockEmbeddedMpvService.getSupport.mockReturnValue({
|
||||
supported: true,
|
||||
});
|
||||
|
||||
const support = getIpcMainHandler(EMBEDDED_MPV_SUPPORT)({});
|
||||
await new Promise<void>((resolve) => setImmediate(resolve));
|
||||
// Linux caches the result of a bare-name `mpv --version`.
|
||||
expect(mockEmbeddedMpvService.getSupport).not.toHaveBeenCalled();
|
||||
|
||||
settle();
|
||||
await expect(support).resolves.toEqual({ supported: true });
|
||||
});
|
||||
|
||||
it('elsewhere, and for session calls, never waits', async () => {
|
||||
Object.defineProperty(process, 'platform', { value: 'darwin' });
|
||||
it.each([EMBEDDED_MPV_SUPPORT, EMBEDDED_MPV_PREPARE])(
|
||||
'%s waits for the lookup before the bare-name mpv probe',
|
||||
async (channel) => {
|
||||
mockEmbeddedMpvService.willProbeLinuxMpvExecutable.mockReturnValue(
|
||||
true
|
||||
);
|
||||
let settle: () => void = () => undefined;
|
||||
mockWaitForLoginShellPath.mockReturnValueOnce(
|
||||
new Promise<void>((resolve) => {
|
||||
settle = resolve;
|
||||
})
|
||||
);
|
||||
mockEmbeddedMpvService.getSupport.mockReturnValue({
|
||||
supported: true,
|
||||
});
|
||||
mockEmbeddedMpvService.prepareAddon.mockReturnValue({
|
||||
supported: true,
|
||||
});
|
||||
|
||||
const support = getIpcMainHandler(channel)({});
|
||||
await new Promise<void>((resolve) => setImmediate(resolve));
|
||||
expect(
|
||||
mockEmbeddedMpvService.getSupport
|
||||
).not.toHaveBeenCalled();
|
||||
expect(
|
||||
mockEmbeddedMpvService.prepareAddon
|
||||
).not.toHaveBeenCalled();
|
||||
|
||||
settle();
|
||||
await expect(support).resolves.toEqual({ supported: true });
|
||||
}
|
||||
);
|
||||
|
||||
it('does not wait when no probe runs, nor for session calls', async () => {
|
||||
mockEmbeddedMpvService.getSupport.mockReturnValue({
|
||||
supported: true,
|
||||
engine: 'frame-copy',
|
||||
});
|
||||
mockEmbeddedMpvService.createSession.mockReturnValue({ id: 's' });
|
||||
|
||||
await getIpcMainHandler(EMBEDDED_MPV_SUPPORT)({});
|
||||
Object.defineProperty(process, 'platform', { value: 'linux' });
|
||||
await getIpcMainHandler(EMBEDDED_MPV_CREATE_SESSION)(
|
||||
{},
|
||||
{ x: 0, y: 0, width: 1, height: 1 }
|
||||
|
||||
@@ -66,23 +66,23 @@ function handleEmbeddedMpv<Args extends unknown[]>(
|
||||
}
|
||||
|
||||
/**
|
||||
* On Linux, support (and prepare, which checks support first) runs
|
||||
* `mpv --version` by bare name and caches the result, so it must see the
|
||||
* login shell PATH. Nothing else here spawns by name.
|
||||
* On Linux, support (and prepare, which checks support first) may run
|
||||
* `mpv --version` by bare name and cache the result, so it must see the
|
||||
* login shell PATH. Only that probe waits; nothing else here spawns by name.
|
||||
*/
|
||||
async function afterLinuxLoginShellPath<T>(check: () => T): Promise<T> {
|
||||
if (process.platform === 'linux') {
|
||||
async function afterLoginShellPathIfProbing<T>(check: () => T): Promise<T> {
|
||||
if (getService().willProbeLinuxMpvExecutable()) {
|
||||
await waitForLoginShellPath();
|
||||
}
|
||||
return check();
|
||||
}
|
||||
|
||||
handleEmbeddedMpv(EMBEDDED_MPV_SUPPORT, () =>
|
||||
afterLinuxLoginShellPath(() => getService().getSupport())
|
||||
afterLoginShellPathIfProbing(() => getService().getSupport())
|
||||
);
|
||||
|
||||
handleEmbeddedMpv(EMBEDDED_MPV_PREPARE, () =>
|
||||
afterLinuxLoginShellPath(() => getService().prepareAddon())
|
||||
afterLoginShellPathIfProbing(() => getService().prepareAddon())
|
||||
);
|
||||
|
||||
handleEmbeddedMpv(
|
||||
|
||||
@@ -375,6 +375,40 @@ describe('EmbeddedMpvNativeService power blocker', () => {
|
||||
expect(support.frameCopyAvailable).toBe(true);
|
||||
});
|
||||
|
||||
it('predicts the bare-name mpv probe exactly when getSupport runs it', () => {
|
||||
Object.defineProperty(process, 'platform', { value: 'linux' });
|
||||
process.env.DISPLAY = ':0';
|
||||
delete process.env.WAYLAND_DISPLAY;
|
||||
mockSpawnSync.mockClear();
|
||||
mockSpawnSync.mockReturnValue({ status: 0 });
|
||||
mockRuntimeUsable();
|
||||
|
||||
expect(service.willProbeLinuxMpvExecutable()).toBe(true);
|
||||
service.getSupport();
|
||||
expect(mockSpawnSync).toHaveBeenCalledWith(
|
||||
'mpv',
|
||||
['--version'],
|
||||
expect.anything()
|
||||
);
|
||||
// Cached afterwards: no further probe, so no further wait.
|
||||
expect(service.willProbeLinuxMpvExecutable()).toBe(false);
|
||||
});
|
||||
|
||||
it('predicts no probe for the frame-copy engine or native Wayland', () => {
|
||||
Object.defineProperty(process, 'platform', { value: 'linux' });
|
||||
process.env.DISPLAY = ':0';
|
||||
process.env.WAYLAND_DISPLAY = 'wayland-0';
|
||||
mockSpawnSync.mockClear();
|
||||
mockRuntimeUsable();
|
||||
expect(service.willProbeLinuxMpvExecutable()).toBe(false);
|
||||
|
||||
delete process.env.WAYLAND_DISPLAY;
|
||||
process.env.IPTVNATOR_ENABLE_EMBEDDED_MPV_FRAME_COPY = '1';
|
||||
expect(service.willProbeLinuxMpvExecutable()).toBe(false);
|
||||
service.getSupport();
|
||||
expect(mockSpawnSync).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('keeps frame-copy supported on Linux without a system mpv executable', () => {
|
||||
// The helper links libmpv itself; the mpv-on-PATH probe only
|
||||
// binds the native --wid engine.
|
||||
|
||||
@@ -377,6 +377,22 @@ export class EmbeddedMpvNativeService {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether `getSupport()` would now run the uncached Linux bare-name
|
||||
* `mpv --version` probe, i.e. reach the native-engine checks: no
|
||||
* frame-copy engine, a usable display server and the feature enabled.
|
||||
* The IPC layer waits for the login shell PATH only then.
|
||||
*/
|
||||
willProbeLinuxMpvExecutable(): boolean {
|
||||
return (
|
||||
process.platform === 'linux' &&
|
||||
this.cachedLinuxMpvExecutableReason === undefined &&
|
||||
!this.isUnsupportedLinuxDisplayServer() &&
|
||||
isEmbeddedMpvFeatureEnabled() &&
|
||||
!this.isFrameCopyEngineActive()
|
||||
);
|
||||
}
|
||||
|
||||
getSupport(): EmbeddedMpvSupport {
|
||||
if (!SUPPORTED_EMBEDDED_MPV_PLATFORMS.has(process.platform)) {
|
||||
return {
|
||||
|
||||
Reference in new issue
Block a user