From bbc854ff31a607eb71b3a8ece3088f4f3d3b956b Mon Sep 17 00:00:00 2001 From: 4gray Date: Thu, 16 Jul 2026 16:24:23 +0200 Subject: [PATCH] fix(embedded-mpv): isolate recording ticks by engine --- ...edded-mpv-player-recording-message.spec.ts | 6 ++- .../embedded-mpv-controls.adapter.ts | 5 +- ...v-player.component.shared-controls.spec.ts | 48 +++++++++++++------ 3 files changed, 42 insertions(+), 17 deletions(-) diff --git a/apps/web/src/app/embedded-mpv-player-recording-message.spec.ts b/apps/web/src/app/embedded-mpv-player-recording-message.spec.ts index a31c13a1a..5aac52ff3 100644 --- a/apps/web/src/app/embedded-mpv-player-recording-message.spec.ts +++ b/apps/web/src/app/embedded-mpv-player-recording-message.spec.ts @@ -32,7 +32,10 @@ describe('EmbeddedMpvPlayerComponent recording status message', () => { } as unknown as typeof window.electron; await TestBed.configureTestingModule({ - imports: [EmbeddedMpvPlayerHostComponent, TranslateModule.forRoot()], + imports: [ + EmbeddedMpvPlayerHostComponent, + TranslateModule.forRoot(), + ], providers: [ { provide: EmbeddedMpvOverlayVisibilityService, @@ -69,6 +72,7 @@ describe('EmbeddedMpvPlayerComponent recording status message', () => { controller.support.set({ supported: true, platform: 'darwin', + engine: 'native', capabilities: { subtitles: true, playbackSpeed: true, diff --git a/libs/ui/playback/src/lib/embedded-mpv-player/embedded-mpv-controls.adapter.ts b/libs/ui/playback/src/lib/embedded-mpv-player/embedded-mpv-controls.adapter.ts index 7af4d54f7..7b14e8db0 100644 --- a/libs/ui/playback/src/lib/embedded-mpv-player/embedded-mpv-controls.adapter.ts +++ b/libs/ui/playback/src/lib/embedded-mpv-player/embedded-mpv-controls.adapter.ts @@ -75,7 +75,9 @@ export class EmbeddedMpvControlsAdapter implements PlayerController { () => this.playbackIdentity() ); private readonly recordingActive = computed( - () => this.controller.session()?.recording?.active === true + () => + this.controller.support()?.engine === 'frame-copy' && + this.controller.session()?.recording?.active === true ); private readonly activeSessionId = computed( () => this.controller.session()?.id ?? null @@ -313,6 +315,7 @@ export class EmbeddedMpvControlsAdapter implements PlayerController { !session || !playbackIdentity || !support?.supported || + support.engine !== 'frame-copy' || support.capabilities?.recording !== true || !this.isLivePlayback(context.playback()) || session?.status === 'error' diff --git a/libs/ui/playback/src/lib/embedded-mpv-player/embedded-mpv-player.component.shared-controls.spec.ts b/libs/ui/playback/src/lib/embedded-mpv-player/embedded-mpv-player.component.shared-controls.spec.ts index 25c7f7711..34589aac2 100644 --- a/libs/ui/playback/src/lib/embedded-mpv-player/embedded-mpv-player.component.shared-controls.spec.ts +++ b/libs/ui/playback/src/lib/embedded-mpv-player/embedded-mpv-player.component.shared-controls.spec.ts @@ -283,23 +283,41 @@ describe('EmbeddedMpvPlayerComponent shared controls host', () => { expect(component.menus.audioOpen()).toBe(true); }); - it('uses only the adapter recording interval for frame-copy', () => { - const { fixture, controller } = render(); - const setInterval = jest.spyOn(window, 'setInterval'); + it.each(['frame-copy', 'native'] as const)( + 'uses exactly one recording interval for the %s engine', + (engine) => { + const { fixture, controller } = render(engine); + const setInterval = jest.spyOn(window, 'setInterval'); - controller.session.update((session) => - session - ? { - ...session, - recording: { - active: true, - startedAt: new Date().toISOString(), - }, - } - : session - ); + controller.session.update((session) => + session + ? { + ...session, + recording: { + active: true, + startedAt: new Date().toISOString(), + }, + } + : session + ); + fixture.detectChanges(); + + expect(setInterval).toHaveBeenCalledTimes(1); + } + ); + + it('keeps the component-scoped recording adapter inert for native', () => { + const { fixture, component, controller } = render('native'); + fixture.componentRef.setInput('playback', { + streamUrl: 'https://example.test/live.ts', + title: 'Live news', + isLive: true, + }); fixture.detectChanges(); + const startRecording = jest.spyOn(controller, 'startRecording'); - expect(setInterval).toHaveBeenCalledTimes(1); + component.sharedControls.commands.toggleRecording(); + + expect(startRecording).not.toHaveBeenCalled(); }); });