diff --git a/libs/ui/playback/src/lib/playback-diagnostics/playback-diagnostics.util.spec.ts b/libs/ui/playback/src/lib/playback-diagnostics/playback-diagnostics.util.spec.ts index 490278b21..10941f557 100644 --- a/libs/ui/playback/src/lib/playback-diagnostics/playback-diagnostics.util.spec.ts +++ b/libs/ui/playback/src/lib/playback-diagnostics/playback-diagnostics.util.spec.ts @@ -293,7 +293,7 @@ describe('playback diagnostics', () => { it('keeps trusted media path extensions ahead of generic query metadata', () => { expect( getPlaybackMediaExtensionFromUrl( - 'http://cdn.example/archive/movie.mp4?type=hls' + 'http://cdn.example/archive/movie.mp4?container=m3u8' ) ).toBe('mp4'); expect( @@ -303,6 +303,19 @@ describe('playback diagnostics', () => { ).toBe('m3u8'); }); + it('keeps explicit extension query metadata ahead of trusted media path extensions', () => { + expect( + getPlaybackMediaExtensionFromUrl( + 'http://cdn.example/live/channel.ts?extension=m3u8' + ) + ).toBe('m3u8'); + expect( + getPlaybackMediaExtensionFromUrl( + 'http://cdn.example/live/channel.m3u8?ext=ts' + ) + ).toBe('ts'); + }); + it('does not infer media extensions from generic type/output query metadata', () => { expect( getPlaybackMediaExtensionFromUrl( @@ -319,6 +332,11 @@ describe('playback diagnostics', () => { 'http://portal.example/play/live.php?format=mpv&stream=123' ) ).toBe(''); + expect( + getPlaybackMediaExtensionFromUrl( + 'http://portal.example/play/live.mpv?stream=123' + ) + ).toBe(''); }); it('detects codecs with limited Chromium browser-player support', () => { diff --git a/libs/ui/playback/src/lib/playback-diagnostics/playback-diagnostics.util.ts b/libs/ui/playback/src/lib/playback-diagnostics/playback-diagnostics.util.ts index caa217f7f..9471bda7f 100644 --- a/libs/ui/playback/src/lib/playback-diagnostics/playback-diagnostics.util.ts +++ b/libs/ui/playback/src/lib/playback-diagnostics/playback-diagnostics.util.ts @@ -124,12 +124,9 @@ const UNSUPPORTED_CONTAINER_NAMES = new Set([ 'x-msvideo', ]); -const DECLARED_MEDIA_EXTENSION_QUERY_KEYS = [ - 'extension', - 'ext', - 'format', - 'container', -]; +const EXPLICIT_MEDIA_EXTENSION_QUERY_KEYS = ['extension', 'ext']; + +const DECLARED_MEDIA_FORMAT_QUERY_KEYS = ['format', 'container']; const DECLARED_MEDIA_EXTENSION_ALIASES = new Map([ ['hls', 'm3u8'], @@ -158,6 +155,7 @@ const NON_MEDIA_URL_EXTENSIONS = new Set([ 'aspx', 'cgi', 'jsp', + 'mpv', 'php', 'pl', ]); @@ -195,14 +193,25 @@ export function createPlaybackSourceMetadata( } export function getPlaybackMediaExtensionFromUrl(url: string): string { + const explicitQueryExtension = getMediaExtensionFromQuery( + url, + EXPLICIT_MEDIA_EXTENSION_QUERY_KEYS + ); + if (explicitQueryExtension) { + return explicitQueryExtension; + } + const pathExtension = normalizeExtensionToken(getExtensionFromUrl(url)); if (DECLARED_MEDIA_EXTENSIONS.has(pathExtension)) { return pathExtension; } - const queryExtension = getMediaExtensionFromQuery(url); - if (queryExtension) { - return queryExtension; + const formatQueryExtension = getMediaExtensionFromQuery( + url, + DECLARED_MEDIA_FORMAT_QUERY_KEYS + ); + if (formatQueryExtension) { + return formatQueryExtension; } if (NON_MEDIA_URL_EXTENSIONS.has(pathExtension)) { @@ -493,10 +502,13 @@ function normalizeExtensionToken(value: string | undefined): string { return normalizeToken(value).replace(/^\.+/, ''); } -function getMediaExtensionFromQuery(url: string): string { +function getMediaExtensionFromQuery( + url: string, + queryKeys: readonly string[] +): string { try { const parsedUrl = new URL(url, 'http://iptvnator.local'); - for (const key of DECLARED_MEDIA_EXTENSION_QUERY_KEYS) { + for (const key of queryKeys) { const declaredExtension = normalizeDeclaredMediaExtension( parsedUrl.searchParams.get(key) ?? undefined );