diff --git a/libs/ui/playback/src/lib/html-video-player/html-video-player.component.spec.ts b/libs/ui/playback/src/lib/html-video-player/html-video-player.component.spec.ts
index 3c2477389..4d79e1461 100644
--- a/libs/ui/playback/src/lib/html-video-player/html-video-player.component.spec.ts
+++ b/libs/ui/playback/src/lib/html-video-player/html-video-player.component.spec.ts
@@ -224,6 +224,35 @@ describe('HtmlVideoPlayerComponent', () => {
);
});
+ it('does not resume the previous source when a channel declares unsupported DRM', () => {
+ const video = component.videoPlayer.nativeElement;
+ const loadSpy = jest
+ .spyOn(video, 'load')
+ .mockImplementation(() => undefined);
+ const playSpy = jest.spyOn(video, 'play').mockResolvedValue(undefined);
+
+ component.playChannel({
+ ...TEST_CHANNEL,
+ url: 'https://stream.example/movie.mp4',
+ });
+ playSpy.mockClear();
+ loadSpy.mockClear();
+
+ component.playChannel({
+ ...TEST_CHANNEL,
+ url: 'https://stream.example/enc.mpd',
+ drm: {
+ licenseType: 'com.widevine.alpha',
+ supported: false,
+ },
+ });
+
+ // No source is loaded for unsupported DRM: play() must not run, and
+ // the element is reset so the old stream cannot keep playing.
+ expect(playSpy).not.toHaveBeenCalled();
+ expect(loadSpy).toHaveBeenCalledTimes(1);
+ });
+
it('emits a playback issue when the native video element reports an unsupported source', () => {
const issues: unknown[] = [];
component.channel = TEST_CHANNEL;
diff --git a/libs/ui/playback/src/lib/html-video-player/html-video-player.component.ts b/libs/ui/playback/src/lib/html-video-player/html-video-player.component.ts
index 8643513a7..815f9881c 100644
--- a/libs/ui/playback/src/lib/html-video-player/html-video-player.component.ts
+++ b/libs/ui/playback/src/lib/html-video-player/html-video-player.component.ts
@@ -197,7 +197,14 @@ export class HtmlVideoPlayerComponent implements OnInit, OnChanges, OnDestroy {
url,
channel.drm
);
- this.handlePlayOperation();
+ if (channel.drm && !channel.drm.supported) {
+ // No source is loaded for unsupported DRM; reset the
+ // element so the previous stream cannot resume playing
+ // underneath the diagnostic banner.
+ this.videoPlayer.nativeElement.load();
+ } else {
+ this.handlePlayOperation();
+ }
} else if (
(extension === 'ts' || !extension) &&
mpegts.isSupported()
diff --git a/libs/ui/playback/src/lib/shaka-engine/shaka-video-session.spec.ts b/libs/ui/playback/src/lib/shaka-engine/shaka-video-session.spec.ts
index c30988adf..e0c1b1379 100644
--- a/libs/ui/playback/src/lib/shaka-engine/shaka-video-session.spec.ts
+++ b/libs/ui/playback/src/lib/shaka-engine/shaka-video-session.spec.ts
@@ -114,7 +114,7 @@ describe('ShakaVideoSession', () => {
expect(session.getPlayer()).toBeNull();
});
- it('drops the external-fallback hint for DRM errors on ClearKey channels', async () => {
+ it('drops the external-fallback hint for every failure on ClearKey channels', async () => {
const environment = createFakeShakaEnvironment();
const session = createSession(environment);
session.start(video, 'http://example.com/enc.mpd', {
@@ -135,6 +135,24 @@ describe('ShakaVideoSession', () => {
// MPV/VLC never receive the KODIPROP license config, so they are not
// offered as a fallback for key failures on ClearKey channels.
expect(issues[0].externalFallbackRecommended).toBe(false);
+
+ // Non-DRM failures (media/codec/manifest/…) are equally unsolvable
+ // externally while the stream itself stays encrypted.
+ session.start(video, 'http://example.com/enc.mpd', {
+ licenseType: 'clearkey',
+ supported: true,
+ clearKeys: { abc: 'def' },
+ });
+ await flush();
+ environment.instances[1].dispatch('error', {
+ severity: 2,
+ category: 3,
+ code: 3016,
+ });
+
+ expect(issues).toHaveLength(2);
+ expect(issues[1].code).toBe(PlaybackDiagnosticCode.MediaDecodeError);
+ expect(issues[1].externalFallbackRecommended).toBe(false);
});
it('emits a diagnostic and tears the engine down when load rejects', async () => {
diff --git a/libs/ui/playback/src/lib/shaka-engine/shaka-video-session.ts b/libs/ui/playback/src/lib/shaka-engine/shaka-video-session.ts
index 8fc365f22..c7696023b 100644
--- a/libs/ui/playback/src/lib/shaka-engine/shaka-video-session.ts
+++ b/libs/ui/playback/src/lib/shaka-engine/shaka-video-session.ts
@@ -228,15 +228,16 @@ export class ShakaVideoSession {
}
/**
- * For channels carrying KODIPROP ClearKey config, DRM failures (wrong or
- * rotated keys, …) cannot be solved by MPV/VLC either — they never
- * receive the license config — so the diagnostic must not offer them.
+ * Channels carrying KODIPROP ClearKey config cannot be handed to MPV/VLC
+ * at all — external players never receive the license config, so the
+ * encrypted stream fails there regardless of what broke inline (DRM,
+ * manifest, codec, network, …). Suppress the fallback hint entirely.
*/
private withoutUnusableDrmFallback(
issue: PlaybackDiagnostic,
drmProvided: boolean
): PlaybackDiagnostic {
- if (!drmProvided || issue.code !== DiagnosticCode.DrmOrEncryption) {
+ if (!drmProvided || !issue.externalFallbackRecommended) {
return issue;
}
return { ...issue, externalFallbackRecommended: false };