From ae4718d88ba65d4a3fd571f08f581d1bf0aa4977 Mon Sep 17 00:00:00 2001 From: 4gray Date: Sat, 1 Aug 2026 11:33:06 +0200 Subject: [PATCH] docs(playback): design structured mpegts diagnostics --- ...01-structured-mpegts-diagnostics-design.md | 301 ++++++++++++++++++ 1 file changed, 301 insertions(+) create mode 100644 docs/superpowers/specs/2026-08-01-structured-mpegts-diagnostics-design.md diff --git a/docs/superpowers/specs/2026-08-01-structured-mpegts-diagnostics-design.md b/docs/superpowers/specs/2026-08-01-structured-mpegts-diagnostics-design.md new file mode 100644 index 000000000..7c0baeab7 --- /dev/null +++ b/docs/superpowers/specs/2026-08-01-structured-mpegts-diagnostics-design.md @@ -0,0 +1,301 @@ +# Structured mpegts.js Diagnostics + +## Context + +PR #1314 stopped treating ambiguous native media errors as codec evidence and +retained explicit HTTP status. PR #1316 added an allowlisted hls.js boundary, +PR #1317 added one for Video.js/VHS, and PR #1318 added one for Shaka Player. +mpegts.js is now the remaining browser source engine whose diagnostic path +serializes arbitrary error payloads and classifies them with substring +heuristics. + +The locked workspace installs mpegts.js `1.8.0`. Its public player error event +has three arguments: error type, error detail, and an information value. The +runtime exports exact `ErrorTypes` and `ErrorDetails` constants. The current +IPTVnator classifier instead lowercases the arguments, serializes arbitrary +`info`, and searches the resulting text for network, access, codec, format, +MSE, and early-EOF fragments. This can misdiagnose failures, retain provider +text, and miss reliable facts such as the HTTP status carried by +`HttpStatusCodeInvalid`. + +All three built-in web players own a mpegts.js integration: + +- `HtmlVideoPlayerComponent` for the built-in HTML5 player; +- `VjsMpegTsSession` for Video.js; +- `ArtPlayerSourceSession` for ArtPlayer. + +They must cross the same evidence boundary so the selected player does not +change the diagnosis for an identical engine event. + +## Goals + +- Add one minimal, allowlisted `MpegTsPlaybackEvidence` boundary shared by all + three mpegts.js owners. +- Version-lock the accepted public constants to installed mpegts.js `1.8.0`. +- Classify only exact, internally consistent public type/detail pairs. +- Surface a validated HTTP 4xx/5xx status for + `NetworkError + HttpStatusCodeInvalid`. +- Preserve the existing useful distinction for unrecoverable early EOF while + replacing text inference with the exact public detail. +- Prevent arbitrary `info`, messages, URLs, headers, bodies, credentials, and + provider objects from reaching stored or rendered diagnostics. +- Shape the evidence so a later player-neutral recommendation layer can use it + without coupling diagnostics to `PlayerController`. + +## Non-goals + +- Building the cross-player recommendation matrix. +- Moving recommendations, diagnostics, or fallback policy into + `PlayerController`. +- Automatic failover, player preference changes, retries beyond those already + owned by mpegts.js, probes, persistence, correlation, or diagnostic history. +- Adding new top-level diagnostic codes or changing diagnostic layout and + translations. +- Inferring CORS, mixed content, Content Security Policy, private-network + access, codec, format, or pipeline stage from message text. +- Inspecting mpegts.js loaders, demuxers, workers, or MediaSource internals at + runtime. +- Changing source selection, live/VOD mode, playback controls, duration + correction, or engine cleanup. + +## Approaches Considered + +### Public allowlisted evidence boundary (selected) + +Normalize the public error-event arguments immediately into a small evidence +object. Accept only exact mpegts.js `1.8.0` type/detail constants, derive stage +and failure from consistent pairs, and read only a validated numeric status +from the documented network error-info slot. Classification and rendering then +receive the sanitized evidence rather than the raw event. + +This is the strongest stable contract available. It fixes HTTP diagnostics, +removes unsafe serialization, keeps all three players consistent, and gives a +future recommendation layer explicit facts instead of prose. + +### Exact constants plus message heuristics + +Keep the public constants but continue reading `info.msg` to infer browser +access or more specific causes. This could preserve a few synthetic CORS cases, +but browser fetch exceptions commonly expose only generic text such as +`Failed to fetch`. Message text is not a stable public taxonomy and may contain +provider data. This approach is rejected. + +### Add HTTP status to the existing classifier + +Special-case `HttpStatusCodeInvalid` while leaving the rest of the substring +classifier unchanged. This would address issue #1159 narrowly but retain the +privacy and accuracy problems for every other mpegts.js failure. It would also +leave no coherent evidence contract for later recommendations. This approach +is rejected. + +## Version-Locked Public Contract + +The production allowlist accepts the exact public constants exported by +mpegts.js `1.8.0`. + +Error types: + +- `NetworkError`; +- `MediaError`; +- `OtherError`. + +Error details: + +- `Exception`; +- `HttpStatusCodeInvalid`; +- `ConnectingTimeout`; +- `UnrecoverableEarlyEof`; +- `MediaMSEError`; +- `FormatError`; +- `FormatUnsupported`; +- `CodecUnsupported`. + +The package also exports loader-only `EarlyEof`, but it is not a public player +`ErrorDetails` value. mpegts.js attempts its finite-source reconnect internally +and exposes only `UnrecoverableEarlyEof` through the player error event when +recovery cannot complete. IPTVnator must not create a terminal diagnostic for +the internal recoverable value. + +A contract test compares the allowlist and package version with the real +installed runtime. A dependency upgrade must fail that test and trigger a new +audit before changed or additional constants are accepted. + +## Evidence Contract + +`MpegTsPlaybackEvidence` contains: + +- `engineType`: exact `NetworkError`, `MediaError`, or `OtherError`, otherwise + `unknown`; +- `engineDetails`: one exact public mpegts.js detail, otherwise `unknown`; +- `disposition`: `terminal`; +- `stage`: `loader`, `demux`, `media-source`, or `unknown`; +- `failure`: `http`, `timeout`, `network`, `truncated-stream`, `format`, + `codec`, `media-source`, or `unknown`; +- optional `httpStatus`: an integer from 400 through 599. + +Stage and failure names are app-owned and stable. Exact engine type and detail +remain available separately so technical output still identifies the public +mpegts.js condition. + +The evidence object does not retain: + +- `info.msg` or any other message; +- arbitrary or serialized `info` values; +- request, response, redirect, or source URLs; +- headers, response text, bodies, events, exceptions, or loader objects; +- provider metadata, tokens, cookies, credentials, or unknown properties. + +The existing `PlaybackDiagnostic.sourceUrl` remains the active source metadata +used by Retry, Copy URL, and explicit external-player actions. No URL is copied +from an engine error. + +## Sanitization And HTTP Status + +`createMpegTsPlaybackEvidence(type, details, info)` validates exact, +case-sensitive public values. Unknown strings, differently cased variants, +numbers, objects, and missing values become `unknown`; message fragments never +upgrade them. + +The boundary reads `info.code` only for the exact +`NetworkError + HttpStatusCodeInvalid` pair. It retains the value only when it +is an integer from 400 through 599. Status-like fields on any other pair, +inside nested objects, or supplied as strings are ignored. No other `info` +property is read or copied. + +The validated status is mirrored to the top-level diagnostic so the existing +metadata badge shows `HTTP 404` or `HTTP 5xx` without a UI redesign. + +## Stage And Failure Mapping + +Only internally consistent public type/detail pairs receive a stage or failure: + +| Public type | Public detail | Stage | Failure | +| --- | --- | --- | --- | +| `NetworkError` | `HttpStatusCodeInvalid` | `loader` | `http` | +| `NetworkError` | `ConnectingTimeout` | `loader` | `timeout` | +| `NetworkError` | `Exception` | `loader` | `network` | +| `NetworkError` | `UnrecoverableEarlyEof` | `loader` | `truncated-stream` | +| `MediaError` | `FormatError` | `demux` | `format` | +| `MediaError` | `FormatUnsupported` | `demux` | `format` | +| `MediaError` | `CodecUnsupported` | `demux` | `codec` | +| `MediaError` | `MediaMSEError` | `media-source` | `media-source` | + +`OtherError`, unknown values, and mismatched pairs keep both fields unknown. +For example, `OtherError + CodecUnsupported` must not become codec evidence. +This fail-closed rule prevents a future engine change or malformed event from +borrowing meaning from only half of the public contract. + +## Lifecycle And Disposition + +The public player `ERROR` event is terminal for IPTVnator's diagnostic +boundary. The installed engine handles recoverable finite-source early EOF +inside `IOController`; only failed recovery becomes +`UnrecoverableEarlyEof` and reaches the player event. Network, demux, and MSE +error paths likewise reach the player event after the active engine operation +has failed. + +IPTVnator does not subscribe to internal recovery events or duplicate engine +retry logic. All accepted evidence therefore records `disposition=terminal`. + +## User-Facing Classification + +`classifyMpegTsPlaybackIssue` consumes sanitized evidence with this mapping: + +1. `failure=http`, `timeout`, or `network` → `network-error`. +2. `failure=truncated-stream` → `media-decode-error`. +3. Exact `MediaError + FormatUnsupported` → `unsupported-container`. +4. Exact `MediaError + CodecUnsupported` → `unsupported-codec`. +5. `failure=format` or `media-source` → `media-decode-error`. +6. Everything else → `unknown-playback-error`. + +The existing code-derived fallback behavior remains intentional: + +- HTTP, timeout, generic network, and unknown failures do not claim that a + different decoder will fix the provider response; +- truncated-stream, unsupported-container, unsupported-codec, format, and + MediaSource failures may offer configured MPV/VLC actions because another + demuxer or media pipeline can plausibly handle the same source. + +No mpegts.js error becomes `browser-access-error`. The public constants do not +distinguish CORS, mixed content, CSP, or private-network access from a generic +fetch exception, and status zero is not sufficient evidence. + +## Runtime Integration + +The raw event must cross the boundary once in each owner: + +- `emitMpegTsPlaybackError` creates evidence for + `HtmlVideoPlayerComponent`; +- `VjsMpegTsSession` creates evidence before emitting its issue; +- `ArtPlayerSourceSession` creates evidence before emitting its issue. + +All three call the same classifier with source metadata that preserves their +existing `InlinePlaybackPlayer` identity. No owner keeps or forwards the raw +event after normalization. Listener binding, stale-engine guards, live/VOD +mode, duration correction, source routing, play calls, and teardown remain +unchanged. + +## Relationship To Shared Controls And Recommendations + +`PlayerController` remains the engine-neutral command/state/capability +contract. It does not own error taxonomy, fallback policy, or provider +evidence. + +Playback diagnostics are a sibling player-neutral layer: engine adapters emit +sanitized evidence, the diagnostic classifier maps it to a user-facing issue, +and the existing viewport renders that issue. A later recommendation layer can +consume diagnostic code, source, engine evidence, source metadata, and runtime +capabilities to explain whether another player is likely to help. That later +work must not require parsing the technical-details string or changing the +controls contract. + +## User Interface + +The existing technical `Error details` row renders only structured mpegts.js +evidence, for example: + +`stage=loader · failure=http · type=NetworkError · details=HttpStatusCodeInvalid · disposition=terminal · HTTP 404` + +When `issue.mpegTs` exists, legacy raw `details` and native message fields are +ignored even if a caller accidentally supplies them. Existing titles, +descriptions, HTTP badge, Retry, Copy URL, external-player actions, layout, and +translations remain unchanged. + +## Testing + +Use test-driven development: + +- Compare the accepted constants and version with the real installed + mpegts.js `1.8.0` runtime. +- Cover every consistent public type/detail mapping. +- Prove mismatched, unknown, differently cased, and malformed inputs remain + unknown. +- Prove only exact `HttpStatusCodeInvalid` can expose a validated HTTP 4xx/5xx + status. +- Prove `info.msg`, nested status, URLs, headers, bodies, exceptions, and + arbitrary provider objects never enter evidence or rendered details. +- Cover HTTP 404, timeout, exception, unrecoverable early EOF, format error, + unsupported format, unsupported codec, MSE error, other, and unknown + classification. +- Cover evidence creation and issue emission through HTML5, Video.js, and + ArtPlayer integrations. +- Prove the rendered detail row uses only structured mpegts.js evidence and + preserves the HTTP metadata badge. + +Run the focused red/green tests, complete `ui-playback` unit and lint targets, +web typecheck, i18n validation, release-note validation, and the repository +test-impact pass. No new E2E case is required because source selection, +playback lifecycle, controls, actions, and diagnostic layout remain unchanged; +the engine event, three adapter boundaries, classifier, and rendered output are +covered directly and deterministically. + +## Documentation And Release Note + +Update `docs/architecture/embedded-inline-playback.md` as the canonical +browser diagnostic contract. Update the mpegts.js diagnostic summaries in +`AGENTS.md` and `CLAUDE.md` because they describe the affected player engines +and evidence boundaries. No `player-controls-contract.md` change is needed +because the controls contract does not change. + +Add a `fix(playback)` note under `.changes/` because users receive accurate HTTP +status, safer technical details, and more precise mpegts.js diagnoses.