fix(playback): treat mpegts early eof as media fallback

Entire-Checkpoint: f957cd9849e0
This commit is contained in:
4gray committed 2026-05-15 00:14:01 +02:00
1 parent c3ef581b6f
commit d190fa0341
4 files changed
+36

No files matched your search

@@ -159,6 +159,8 @@ Supported diagnostic codes are:
`network-error` is reserved for provider/network loading failures. Browser security failures such as CORS, mixed content, Content Security Policy, and private-network-access blocks are classified as `browser-access-error` so the UI can explain that the browser player was blocked before playback reached decoding.
mpegts.js `Early-EOF` failures on MPEG-TS streams are classified as `media-decode-error` instead of generic `network-error`. These failures usually mean the fetch stream ended before mpegts.js expected a complete transport stream, and external players may still handle the same URL more tolerant of short reads or malformed TS boundaries.
The banner keeps the primary message compact, then exposes technical details on demand: diagnostic code, reporting player/source, detected container/MIME, video/audio codecs, native browser error fields, and raw HLS/mpegts details. HLS manifest codec metadata also drives a concise browser-support hint for codecs that Chromium/Electron commonly cannot decode inline, such as HEVC, AC-3, E-AC-3, DTS, and MPEG-2 video.
URL extension metadata is filtered before diagnostics and player selection use it. Web script extensions such as `.php` are not shown as stream containers; explicit media query metadata such as `extension=ts` or `format=m3u8` is preferred when present.
@@ -244,6 +244,25 @@ describe('playback diagnostics', () => {
expect(issue.externalFallbackRecommended).toBe(true);
});
it('classifies mpegts early EOF failures as fallback-actionable media errors', () => {
const issue = classifyMpegTsPlaybackIssue(
{
type: 'NetworkError',
details: 'UnrecoverableEarlyEof',
info: { msg: 'Fetch stream meet Early-EOF' },
},
createPlaybackSourceMetadata({
url: 'https://provider.example/movie/123.ts',
mimeType: 'video/mp2t',
player: 'videojs',
})
);
expect(issue.code).toBe(PlaybackDiagnosticCode.MediaDecodeError);
expect(issue.externalFallbackRecommended).toBe(true);
expect(issue.details).toContain('Early-EOF');
});
it('classifies mpegts codec errors as unsupported codec fallbacks', () => {
const issue = classifyMpegTsPlaybackIssue(
{
@@ -13,6 +13,7 @@ import {
isBrowserAccessFailure,
isCodecFailure,
isDrmOrEncryptionFailure,
isEarlyEofFailure,
isNetworkFailure,
normalizeErrorDetails,
} from './playback-error-patterns.util';
@@ -152,6 +153,15 @@ export function classifyMpegTsPlaybackIssue(
const lowerDetails = details.toLowerCase();
const lowerType = (error.type ?? '').toLowerCase();
if (isEarlyEofFailure(lowerDetails)) {
return createDiagnostic({
code: DiagnosticCode.MediaDecodeError,
source: DiagnosticSource.MpegTs,
metadata,
details,
});
}
if (isNetworkFailure(lowerType, lowerDetails)) {
return createDiagnostic({
code: isBrowserAccessFailure(lowerDetails)
@@ -45,6 +45,11 @@ export function isBrowserAccessFailure(details: string): boolean {
);
}
export function isEarlyEofFailure(details: string): boolean {
const compactDetails = details.replace(/[^a-z0-9]/g, '');
return compactDetails.includes('earlyeof');
}
export function isCodecFailure(details: string): boolean {
return (
details.includes('codec') ||