mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-13 04:26:16 -08:00
fix(playback): retain mounted engine when saved player becomes MPV/VLC
With the saved player now live-tracked, a mid-session palette switch to managed MPV/VLC cleared the inline binding on hosts without a playerOverride and left a blank viewport — the web player view can neither render nor launch external players. resolveRenderableWebPlayer keeps the mounted engine in that case; the external choice applies when the host starts the next playback. Renderable players, including Embedded MPV, still apply live. Raised by Codex review (P2). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6eab0938ea
commit
f44844fb6c
4 files changed
+74
-5
No files matched your search
@@ -375,7 +375,10 @@ mounted `WebPlayerViewComponent` in place as a new playback application, and a
|
||||
first mount reads the already-loaded store value so the default engine never
|
||||
flashes before the saved one. Hosts that pass no `playerOverride` (the Xtream
|
||||
and Stalker live layouts, the portal inline detail player) rely on this live
|
||||
tracking.
|
||||
tracking. A saved switch to managed MPV/VLC is the one exception
|
||||
(`resolveRenderableWebPlayer`): the viewport can neither render nor launch an
|
||||
external player, so the mounted engine is retained and the external choice
|
||||
applies when the host starts the next playback.
|
||||
|
||||
Video.js, HTML5, and ArtPlayer
|
||||
report native media errors, hls.js errors, Video.js/VHS errors, Shaka errors,
|
||||
|
||||
@@ -208,3 +208,19 @@ export function toInlinePlaybackPlayer(
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Managed MPV/VLC cannot render inside the web player viewport and the view
|
||||
* never launches them itself, so a mid-session saved-player switch to one
|
||||
* retains whatever engine is already mounted — the external choice applies
|
||||
* when the host starts the next playback. Every renderable player, including
|
||||
* Embedded MPV, is applied directly.
|
||||
*/
|
||||
export function resolveRenderableWebPlayer(
|
||||
configured: VideoPlayer,
|
||||
previous?: { value: VideoPlayer }
|
||||
): VideoPlayer {
|
||||
const external =
|
||||
configured === VideoPlayer.MPV || configured === VideoPlayer.VLC;
|
||||
return external && previous ? previous.value : configured;
|
||||
}
|
||||
@@ -656,6 +656,47 @@ describe('WebPlayerViewComponent', () => {
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it('retains the mounted engine when the saved player becomes MPV/VLC', async () => {
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(
|
||||
fixture.debugElement.query(By.directive(StubVjsPlayerComponent))
|
||||
).not.toBeNull();
|
||||
|
||||
// The view can neither render nor launch an external player, so
|
||||
// the switch must not blank the viewport; it applies when the
|
||||
// host starts the next playback.
|
||||
await TestBed.inject(SettingsStore).updateSettings({
|
||||
player: VideoPlayer.MPV,
|
||||
});
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(
|
||||
fixture.debugElement.query(By.directive(StubVjsPlayerComponent))
|
||||
).not.toBeNull();
|
||||
|
||||
// A later inline choice still applies live.
|
||||
await TestBed.inject(SettingsStore).updateSettings({
|
||||
player: VideoPlayer.Html5Player,
|
||||
});
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(
|
||||
fixture.debugElement.query(By.directive(StubVjsPlayerComponent))
|
||||
).toBeNull();
|
||||
expect(
|
||||
fixture.debugElement.query(
|
||||
By.directive(StubHtmlVideoPlayerComponent)
|
||||
)
|
||||
).not.toBeNull();
|
||||
});
|
||||
|
||||
it('keeps an explicit playerOverride ahead of the saved player', async () => {
|
||||
fixture.componentRef.setInput(
|
||||
'playerOverride',
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
effect,
|
||||
inject,
|
||||
input,
|
||||
linkedSignal,
|
||||
output,
|
||||
signal,
|
||||
untracked,
|
||||
@@ -55,6 +56,7 @@ import { resolveWebPlayerMediaTitle } from './web-player-playback-state';
|
||||
import {
|
||||
createWebPlayerRecommendations,
|
||||
isPlaybackExternallyTransferable,
|
||||
resolveRenderableWebPlayer,
|
||||
toInlinePlaybackPlayer,
|
||||
toVideoPlayer,
|
||||
} from './web-player-recovery-policy';
|
||||
@@ -143,14 +145,21 @@ export class WebPlayerViewComponent implements OnDestroy {
|
||||
|
||||
// Resolved from the live SettingsStore signal, not a mount-time storage
|
||||
// snapshot: a saved player change (settings page, command palette) must
|
||||
// reach an already-mounted player without a remount.
|
||||
// reach an already-mounted player without a remount. A saved managed
|
||||
// MPV/VLC retains the mounted engine (resolveRenderableWebPlayer),
|
||||
// because this view can neither render nor launch external players.
|
||||
private readonly renderablePlayer = linkedSignal({
|
||||
source: () =>
|
||||
this.playerOverride() ??
|
||||
this.settingsStore.player?.() ??
|
||||
VideoPlayer.VideoJs,
|
||||
computation: resolveRenderableWebPlayer,
|
||||
});
|
||||
readonly selectedPlayer = computed<VideoPlayer>(() => {
|
||||
const temporary = this.recoverySession.temporaryPlayerOverride();
|
||||
return temporary
|
||||
? toVideoPlayer(temporary)
|
||||
: (this.playerOverride() ??
|
||||
this.settingsStore.player?.() ??
|
||||
VideoPlayer.VideoJs);
|
||||
: this.renderablePlayer();
|
||||
});
|
||||
private readonly applicationState = createWebPlayerApplicationState({
|
||||
playback: this.playback,
|
||||
|
||||
Reference in new issue
Block a user