docs(playback): design structured HLS diagnostics

This commit is contained in:
4gray committed 2026-07-30 21:58:14 +02:00
1 parent bf13849d69
commit fc393e5ec7
1 file changed
+210
@@ -0,0 +1,210 @@
# Structured HLS Diagnostics
## Context
PR #1314 made native media diagnostics evidence-based, but the HLS path still
normalizes `type`, `details`, `message`, and an arbitrary `error` payload into
one string. `classifyHlsPlaybackIssue` then searches that string for broad
fragments such as `network`, `status`, and `codec`. This can misclassify a
provider message, and serializing arbitrary values can retain URLs,
credentials, response content, loader context, or other provider data.
IPTVnator currently installs hls.js 1.6.16. Its public `ErrorData` supplies
structured `type`, `details`, `fatal`, and an optional `response.code`.
Specific `ErrorDetails` values also identify the manifest, level, segment, key,
or media stage. The same payload contains unsafe or unbounded fields, including
`error`, `reason`, `networkDetails`, `context`, `loader`, `url`, and response
URL/text/data, which are not needed for classification.
Both the HTML5 player and ArtPlayer subscribe to `Hls.Events.ERROR`. They
currently ignore `fatal: false` before calling the classifier, but each adapter
builds the broad string input independently.
## Goals
- Create one structured HLS evidence boundary for the installed hls.js error
shape.
- Retain only allowlisted engine type/detail identifiers, the final
fatal/recoverable disposition, a stage derived from exact details, a
structured failure kind, and a validated HTTP status when present.
- Classify HLS failures from exact engine values rather than substring
heuristics.
- Keep insufficient evidence explicitly unknown.
- Ensure recoverable hls.js events never become terminal playback diagnostics.
- Use the same extraction and classification path in HTML5 and ArtPlayer.
- Show only the sanitized evidence in technical diagnostic details.
## Non-goals
- Diagnostic history, persistence, correlation, or telemetry.
- Additional manifest or stream probes.
- Automatic player failover.
- A Shaka or mpegts.js diagnostic redesign.
- A cross-player confidence or likelihood matrix.
- Inferring browser access failures from status zero, `TypeError`, or error
message text.
- Changing hls.js retry, level-switch, or recovery behavior.
## Approaches Considered
### Boundary sanitizer (selected)
Convert hls.js `ErrorData` into a small `HlsPlaybackEvidence` value at the
adapter boundary. The classifier accepts only that value. This centralizes the
allowlist, makes unsafe fields structurally unavailable to classification and
UI code, and gives HTML5 and ArtPlayer identical behavior.
### Pass `ErrorData` directly
This would preserve the strongest TypeScript relationship with hls.js, but it
would leave every consumer able to read or render URLs, loader state, headers,
response payloads, and arbitrary errors. It also makes privacy depend on each
future call site remembering the same exclusions.
### Keep the broad input and tighten string tests
Exact comparisons could improve classification, but the adapters would still
copy arbitrary messages and objects into diagnostics. This would not establish
the requested safe evidence contract.
## Evidence Contract
`HlsPlaybackEvidence` contains:
- `engineType`: one of the installed hls.js `ErrorTypes` values, otherwise
`unknown`;
- `engineDetails`: one of the installed hls.js `ErrorDetails` values,
including its existing `unknown` value;
- `disposition`: `fatal` when `ErrorData.fatal === true`, otherwise
`recoverable`;
- `stage`: `manifest`, `level`, `segment`, `key`, `media`, or `unknown`;
- `failure`: `http`, `timeout`, `network`, `access`, or `unknown`;
- optional `httpStatus`: an integer from 100 through 599 copied only from
`ErrorData.response.code`.
The `access` value is part of the contract but hls.js 1.6.16 does not expose a
reliable structured CORS, mixed-content, CSP, or private-network-access code.
Status zero and generic fetch failures can also mean DNS, offline, connection,
or abort failures. The extractor therefore does not emit `access` for current
runtime shapes; it uses `network` or `unknown` and never inspects message text.
The extractor does not copy:
- `ErrorData.url`;
- `context`, including request URLs and headers;
- `networkDetails` or loader instances;
- `response.url`, `response.text`, or `response.data`;
- `error`, deprecated `err`, `reason`, events, fragments, or arbitrary values.
The existing playback metadata still carries the active source URL for the
pre-existing Retry, Copy URL, and explicit external-player workflows. No HLS
error URL is copied into evidence or shown in technical details.
## Stage Mapping
Stages are derived only from exact `ErrorDetails` values:
- `manifest`: manifest load, timeout, parsing, and incompatible-codec details;
- `level`: level load, timeout, parsing, empty, and switch details, plus
audio/subtitle track playlist load details;
- `segment`: fragment load, timeout, parsing, decrypt, and gap details;
- `key`: key load/timeout and key-system details;
- `media`: buffer, mux/remux, attach-media, and playback-stall details;
- `unknown`: internal, aborted, interstitial, unrecognized, or otherwise
unmapped details.
This mapping describes the engine stage, not root-cause likelihood. For
example, a segment-stage timeout is still classified as a network timeout.
## Failure Mapping
Failure kind uses this precedence:
1. Exact timeout details produce `timeout`.
2. A validated 4xx or 5xx `response.code` produces `http`.
3. Exact network load-error details, or the exact `networkError` engine type,
produce `network`.
4. No current hls.js 1.6.16 field independently proves `access`.
5. Everything else is `unknown`.
A 1xx, 2xx, or 3xx status remains retained as factual response metadata but
does not by itself produce an HTTP-failure kind.
## Classification
`classifyHlsPlaybackIssue` returns `PlaybackDiagnostic | null`:
1. `recoverable` returns `null` before classification.
2. Exact incompatible/add-codec details produce `unsupported-codec`.
3. Exact key-system or fragment-decrypt evidence produces
`drm-or-encryption`.
4. Exact network type, load/timeout details, or HTTP failure evidence produces
`network-error`.
5. Exact media or mux type produces `media-decode-error`.
6. Everything else produces `unknown-playback-error`.
The HLS path does not emit `browser-access-error` without structured access
evidence. It also does not classify `keyLoadError` as DRM: failure to fetch an
encryption key is network evidence, while `fragDecryptError` and key-system
details are encryption/DRM evidence.
The normalized `PlaybackDiagnostic` retains the sanitized HLS evidence and
copies its validated HTTP status into the existing top-level `httpStatus`
field. External fallback recommendations continue to derive from the final
diagnostic code.
## Adapter Flow
HTML5 and ArtPlayer both use:
1. `createHlsPlaybackEvidence(data)`;
2. `classifyHlsPlaybackIssue(evidence, sourceMetadata)`;
3. emit only when the classifier returns a diagnostic.
The fatal gate therefore lives in the shared classifier rather than being
duplicated in the two adapters. hls.js registers its final error-controller
listener during construction, before IPTVnator subscribes, so the application
observes the final `fatal` value after retry/alternate handling has had a
chance to update it.
## User Interface
The existing technical “Error details” row renders a deterministic, safe HLS
summary made only from the evidence fields, for example:
`stage=manifest · failure=http · type=networkError · details=manifestLoadError · disposition=fatal · HTTP 404`
No HLS error message, URL, header, body, response text, or arbitrary provider
payload appears. Existing title/description and HTTP metadata rendering remain
unchanged, so no new translation keys or layout changes are required.
## Testing
Use test-driven development:
- Add extractor tests using hls.js 1.6.16-shaped manifest, level, segment, key,
media, HTTP, timeout, and unknown payloads.
- Prove unrecognized runtime values become `unknown`.
- Prove response URLs/text/data, request context/headers, error messages,
network details, and credential-shaped payloads do not appear in evidence or
rendered details.
- Add classifier tests for exact codec, DRM, network, media, and unknown
values.
- Prove misleading strings cannot override structured engine values.
- Prove recoverable events return `null`.
- Update HTML5 and ArtPlayer integration tests to cover fatal HTTP evidence and
recoverable suppression through the shared path.
- Add a focused view test for the deterministic safe HLS summary.
Run the complete `ui-playback` unit target, its lint target, the web typecheck,
i18n drift validation, and release-note validation. No E2E is required because
the player workflow and diagnostic controls are unchanged; adapter, classifier,
and rendered-detail behavior are covered at their closest unit/component
boundaries.
## Documentation And Release Note
Update `docs/architecture/embedded-inline-playback.md`, the canonical playback
diagnostic description, to replace raw HLS details with the structured
contract. Add a `fix(playback)` release note because users receive more accurate
HLS diagnoses and safer technical details.