Files
iptvnator/libs/ui/playback/src/lib/playback-diagnostics/playback-error-patterns.util.ts
T
4gray 4a4334cb64 feat(playback): improve browser-player diagnostics (#939)
* feat(playback): diagnose browser stream access errors
Entire-Checkpoint: f957cd9849e0

* feat(playback): expose browser-player diagnostic details
Entire-Checkpoint: f957cd9849e0

* fix(playback): keep media path extensions authoritative
Entire-Checkpoint: f957cd9849e0

* fix(playback): tighten browser access diagnostics
Entire-Checkpoint: f957cd9849e0

* fix(playback): align diagnostic followups
Entire-Checkpoint: f957cd9849e0

* fix(playback): narrow declared stream metadata
Entire-Checkpoint: f957cd9849e0

* fix(playback): preserve explicit extension overrides
Entire-Checkpoint: f957cd9849e0

* fix(playback): keep mpeg-ts out of unsupported containers
Entire-Checkpoint: f957cd9849e0

* refactor(playback): split diagnostics utilities
Entire-Checkpoint: f957cd9849e0

* fix(playback): treat mpegts early eof as media fallback
Entire-Checkpoint: f957cd9849e0
2026-05-15 00:55:23 +02:00

97 lines
2.9 KiB
TypeScript

import type {
HlsPlaybackErrorInput,
MpegTsPlaybackErrorInput,
} from './playback-diagnostics.model';
export function normalizeErrorDetails(
error: HlsPlaybackErrorInput | MpegTsPlaybackErrorInput
): string {
const hlsError = 'error' in error ? error.error : undefined;
const mpegTsInfo = 'info' in error ? error.info : undefined;
const errorMessage = normalizeErrorPayload(hlsError);
const info = normalizeErrorPayload(mpegTsInfo);
return [error.details, error.message, errorMessage, info]
.filter((part): part is string => Boolean(part))
.join(' ');
}
export function isNetworkFailure(type: string, details: string): boolean {
return (
type.includes('network') ||
details.includes('network') ||
details.includes('loaderror') ||
details.includes('timeout') ||
details.includes('status')
);
}
export function isBrowserAccessFailure(details: string): boolean {
return (
details.includes('cors') ||
details.includes('cross-origin') ||
details.includes('cross origin') ||
details.includes('access-control') ||
details.includes('access control') ||
details.includes('content security policy') ||
details.includes('mixed content') ||
details.includes('private network access') ||
details.includes('blocked by content security') ||
details.includes('blocked by cors') ||
details.includes('blocked by mixed content') ||
details.includes('not allowed to load local resource') ||
details.includes('err_blocked') ||
details.includes('err_cleartext')
);
}
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') ||
details.includes('incompatiblecodecs') ||
details.includes('addcodec')
);
}
export function isDrmOrEncryptionFailure(details: string): boolean {
return (
details.includes('decrypt') ||
details.includes('keysystem') ||
details.includes('keyload') ||
details.includes('license') ||
details.includes('drm')
);
}
function normalizeErrorPayload(payload: unknown): string {
if (!payload) {
return '';
}
if (typeof payload === 'string') {
return payload;
}
if (payload instanceof Error) {
const extraDetails = stringifyUnknown(payload);
return [payload.message, extraDetails === '{}' ? '' : extraDetails]
.filter(Boolean)
.join(' ');
}
return stringifyUnknown(payload);
}
function stringifyUnknown(value: unknown): string {
try {
return JSON.stringify(value) || '';
} catch {
return String(value);
}
}