Files
iptvnator/docs/superpowers/specs/2026-07-30-structured-hls-diagnostics-design.md
T
4gray 99d167993d fix(playback): structure HLS diagnostics (#1316)
* docs(playback): design structured HLS diagnostics

* docs(playback): plan structured HLS diagnostics

* fix(playback): structure HLS diagnostics

* docs(playback): document structured HLS evidence

* fix(playback): keep HLS startup logs private
2026-07-31 07:33:05 +02:00

9.1 KiB

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. HLS startup development logs are event-only and omit provider-supplied channel names and source URLs. 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.