fix(playback): preserve explicit extension overrides

Entire-Checkpoint: f957cd9849e0
This commit is contained in:
4gray committed 2026-05-14 22:32:37 +02:00
1 parent cd331c796c
commit a0850c1505
2 files changed
+42 -12

No files matched your search

@@ -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', () => {
@@ -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
);