mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-09 17:36:15 -08:00
docs(playback): plan structured mpegts diagnostics
This commit is contained in:
1 parent
ae4718d88b
commit
0c377e3ef5
1 file changed
+783
@@ -0,0 +1,783 @@
|
||||
# Structured mpegts.js Diagnostics Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Replace mpegts.js message heuristics with a version-locked,
|
||||
allowlisted evidence boundary shared by HTML5, Video.js, and ArtPlayer.
|
||||
|
||||
**Architecture:** Normalize only mpegts.js 1.8.0 public error type/detail
|
||||
constants and the exact HTTP-status slot into `MpegTsPlaybackEvidence`. The
|
||||
shared classifier maps internally consistent evidence to existing diagnostic
|
||||
codes, and the existing viewport renders only structured evidence while the
|
||||
three engine owners keep their current source and lifecycle behavior.
|
||||
|
||||
**Tech Stack:** Angular 21, TypeScript 5.9, mpegts.js 1.8.0, Jest through Nx,
|
||||
Markdown architecture and release-note documentation.
|
||||
|
||||
---
|
||||
|
||||
### Task 0: Confirm The Isolated Baseline And Public Runtime
|
||||
|
||||
**Files:**
|
||||
|
||||
- Verify: `package.json`
|
||||
- Verify: `pnpm-lock.yaml`
|
||||
- Verify: `node_modules/mpegts.js/src/player/player-errors.js`
|
||||
- Verify: `node_modules/mpegts.js/src/io/loader.js`
|
||||
- Verify: `node_modules/mpegts.js/src/core/transmuxing-controller.js`
|
||||
- Verify: `node_modules/mpegts.js/d.ts/mpegts.d.ts`
|
||||
|
||||
- [x] **Step 1: Create the isolated feature branch from current master**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
git switch -c agent/structure-mpegts-diagnostics origin/master
|
||||
```
|
||||
|
||||
Expected: branch starts at `9f4e11d6d`, the merge of PR #1318.
|
||||
|
||||
- [x] **Step 2: Verify dependency and Nx availability**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
test -d node_modules
|
||||
pnpm nx show projects --withTarget test
|
||||
```
|
||||
|
||||
Expected: exit 0 and output containing `ui-playback`.
|
||||
|
||||
- [x] **Step 3: Run the affected-project baseline**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
pnpm nx test ui-playback
|
||||
```
|
||||
|
||||
Expected: 87 suites and 849 tests pass before implementation.
|
||||
|
||||
- [x] **Step 4: Audit the installed public contract**
|
||||
|
||||
Confirm:
|
||||
|
||||
```text
|
||||
mpegts.js version = 1.8.0
|
||||
ErrorTypes = NetworkError, MediaError, OtherError
|
||||
ErrorDetails = Exception, HttpStatusCodeInvalid, ConnectingTimeout,
|
||||
UnrecoverableEarlyEof, MediaMSEError, FormatError,
|
||||
FormatUnsupported, CodecUnsupported
|
||||
HttpStatusCodeInvalid info = { code: HTTP status, msg: status text }
|
||||
recoverable EarlyEof is handled internally before the public player error
|
||||
```
|
||||
|
||||
### Task 1: Drive The Public Evidence Boundary From Failing Tests
|
||||
|
||||
**Files:**
|
||||
|
||||
- Create:
|
||||
`libs/ui/playback/src/lib/playback-diagnostics/mpegts-playback-evidence.model.ts`
|
||||
- Create:
|
||||
`libs/ui/playback/src/lib/playback-diagnostics/mpegts-playback-evidence.util.ts`
|
||||
- Create:
|
||||
`libs/ui/playback/src/lib/playback-diagnostics/mpegts-playback-evidence.util.spec.ts`
|
||||
|
||||
- [ ] **Step 1: Write the failing installed-runtime contract test**
|
||||
|
||||
Create `mpegts-playback-evidence.util.spec.ts` and assert the real installed
|
||||
runtime before using any mocks:
|
||||
|
||||
```typescript
|
||||
import mpegts from 'mpegts.js';
|
||||
import {
|
||||
MpegTsPlaybackEngineDetails,
|
||||
MpegTsPlaybackEngineType,
|
||||
} from './mpegts-playback-evidence.model';
|
||||
import {
|
||||
MPEGTS_DIAGNOSTIC_VERSION,
|
||||
createMpegTsPlaybackEvidence,
|
||||
} from './mpegts-playback-evidence.util';
|
||||
|
||||
describe('mpegts.js playback evidence', () => {
|
||||
it('locks the accepted public contract to mpegts.js 1.8.0', () => {
|
||||
expect(mpegts.version).toBe(MPEGTS_DIAGNOSTIC_VERSION);
|
||||
expect(mpegts.ErrorTypes).toEqual({
|
||||
NETWORK_ERROR: MpegTsPlaybackEngineType.Network,
|
||||
MEDIA_ERROR: MpegTsPlaybackEngineType.Media,
|
||||
OTHER_ERROR: MpegTsPlaybackEngineType.Other,
|
||||
});
|
||||
expect(mpegts.ErrorDetails).toEqual({
|
||||
NETWORK_EXCEPTION:
|
||||
MpegTsPlaybackEngineDetails.NetworkException,
|
||||
NETWORK_STATUS_CODE_INVALID:
|
||||
MpegTsPlaybackEngineDetails.HttpStatusCodeInvalid,
|
||||
NETWORK_TIMEOUT:
|
||||
MpegTsPlaybackEngineDetails.ConnectingTimeout,
|
||||
NETWORK_UNRECOVERABLE_EARLY_EOF:
|
||||
MpegTsPlaybackEngineDetails.UnrecoverableEarlyEof,
|
||||
MEDIA_MSE_ERROR: MpegTsPlaybackEngineDetails.MediaMseError,
|
||||
MEDIA_FORMAT_ERROR: MpegTsPlaybackEngineDetails.FormatError,
|
||||
MEDIA_FORMAT_UNSUPPORTED:
|
||||
MpegTsPlaybackEngineDetails.FormatUnsupported,
|
||||
MEDIA_CODEC_UNSUPPORTED:
|
||||
MpegTsPlaybackEngineDetails.CodecUnsupported,
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
NODE_OPTIONS=--experimental-vm-modules node node_modules/jest/bin/jest.js \
|
||||
--config jest.web-esm.workspace.ts --runTestsByPath \
|
||||
libs/ui/playback/src/lib/playback-diagnostics/mpegts-playback-evidence.util.spec.ts \
|
||||
--runInBand
|
||||
```
|
||||
|
||||
Expected: FAIL because the model and evidence modules do not exist.
|
||||
|
||||
- [ ] **Step 2: Add the focused evidence model**
|
||||
|
||||
Create `mpegts-playback-evidence.model.ts` with const-derived unions:
|
||||
|
||||
```typescript
|
||||
export const MpegTsPlaybackEngineType = {
|
||||
Network: 'NetworkError',
|
||||
Media: 'MediaError',
|
||||
Other: 'OtherError',
|
||||
Unknown: 'unknown',
|
||||
} as const;
|
||||
|
||||
export type MpegTsPlaybackEngineType =
|
||||
(typeof MpegTsPlaybackEngineType)[keyof typeof MpegTsPlaybackEngineType];
|
||||
|
||||
export const MpegTsPlaybackEngineDetails = {
|
||||
NetworkException: 'Exception',
|
||||
HttpStatusCodeInvalid: 'HttpStatusCodeInvalid',
|
||||
ConnectingTimeout: 'ConnectingTimeout',
|
||||
UnrecoverableEarlyEof: 'UnrecoverableEarlyEof',
|
||||
MediaMseError: 'MediaMSEError',
|
||||
FormatError: 'FormatError',
|
||||
FormatUnsupported: 'FormatUnsupported',
|
||||
CodecUnsupported: 'CodecUnsupported',
|
||||
Unknown: 'unknown',
|
||||
} as const;
|
||||
|
||||
export type MpegTsPlaybackEngineDetails =
|
||||
(typeof MpegTsPlaybackEngineDetails)[keyof typeof MpegTsPlaybackEngineDetails];
|
||||
|
||||
export const MpegTsPlaybackDisposition = { Terminal: 'terminal' } as const;
|
||||
export type MpegTsPlaybackDisposition =
|
||||
(typeof MpegTsPlaybackDisposition)[keyof typeof MpegTsPlaybackDisposition];
|
||||
export const MpegTsPlaybackStage = {
|
||||
Loader: 'loader',
|
||||
Demux: 'demux',
|
||||
MediaSource: 'media-source',
|
||||
Unknown: 'unknown',
|
||||
} as const;
|
||||
export type MpegTsPlaybackStage =
|
||||
(typeof MpegTsPlaybackStage)[keyof typeof MpegTsPlaybackStage];
|
||||
export const MpegTsPlaybackFailure = {
|
||||
Http: 'http',
|
||||
Timeout: 'timeout',
|
||||
Network: 'network',
|
||||
TruncatedStream: 'truncated-stream',
|
||||
Format: 'format',
|
||||
Codec: 'codec',
|
||||
MediaSource: 'media-source',
|
||||
Unknown: 'unknown',
|
||||
} as const;
|
||||
export type MpegTsPlaybackFailure =
|
||||
(typeof MpegTsPlaybackFailure)[keyof typeof MpegTsPlaybackFailure];
|
||||
|
||||
export interface MpegTsPlaybackEvidence {
|
||||
readonly engineType: MpegTsPlaybackEngineType;
|
||||
readonly engineDetails: MpegTsPlaybackEngineDetails;
|
||||
readonly disposition: MpegTsPlaybackDisposition;
|
||||
readonly stage: MpegTsPlaybackStage;
|
||||
readonly failure: MpegTsPlaybackFailure;
|
||||
readonly httpStatus?: number;
|
||||
}
|
||||
```
|
||||
|
||||
Use const-derived aliases for disposition, stage, and failure rather than
|
||||
duplicating string unions if TypeScript reports drift.
|
||||
|
||||
- [ ] **Step 3: Add failing sanitization and mapping cases**
|
||||
|
||||
Extend the spec with a table covering every consistent pair and expected
|
||||
stage/failure. Add explicit cases proving:
|
||||
|
||||
```typescript
|
||||
expect(
|
||||
createMpegTsPlaybackEvidence('NetworkError', 'HttpStatusCodeInvalid', {
|
||||
code: 404,
|
||||
msg: 'Not Found secret',
|
||||
url: 'https://provider.example/live.ts?token=secret',
|
||||
})
|
||||
).toEqual({
|
||||
engineType: 'NetworkError',
|
||||
engineDetails: 'HttpStatusCodeInvalid',
|
||||
disposition: 'terminal',
|
||||
stage: 'loader',
|
||||
failure: 'http',
|
||||
httpStatus: 404,
|
||||
});
|
||||
|
||||
expect(
|
||||
createMpegTsPlaybackEvidence('OtherError', 'CodecUnsupported', {
|
||||
code: 503,
|
||||
})
|
||||
).toEqual({
|
||||
engineType: 'OtherError',
|
||||
engineDetails: 'CodecUnsupported',
|
||||
disposition: 'terminal',
|
||||
stage: 'unknown',
|
||||
failure: 'unknown',
|
||||
});
|
||||
```
|
||||
|
||||
Also assert status strings, 399, 600, nested `code`, lowercase constants,
|
||||
unknown objects, and circular payloads do not add `httpStatus` or throw.
|
||||
|
||||
Run the focused spec. Expected: FAIL because the factory is missing.
|
||||
|
||||
- [ ] **Step 4: Implement the minimal sanitizer**
|
||||
|
||||
Create `mpegts-playback-evidence.util.ts` with:
|
||||
|
||||
```typescript
|
||||
export const MPEGTS_DIAGNOSTIC_VERSION = '1.8.0';
|
||||
|
||||
export function createMpegTsPlaybackEvidence(
|
||||
type: unknown,
|
||||
details: unknown,
|
||||
info: unknown
|
||||
): MpegTsPlaybackEvidence {
|
||||
const engineType = normalizeEngineType(type);
|
||||
const engineDetails = normalizeEngineDetails(details);
|
||||
const { stage, failure } = deriveMpegTsStageAndFailure(
|
||||
engineType,
|
||||
engineDetails
|
||||
);
|
||||
const httpStatus =
|
||||
engineType === MpegTsPlaybackEngineType.Network &&
|
||||
engineDetails ===
|
||||
MpegTsPlaybackEngineDetails.HttpStatusCodeInvalid
|
||||
? readHttpStatus(info)
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
engineType,
|
||||
engineDetails,
|
||||
disposition: MpegTsPlaybackDisposition.Terminal,
|
||||
stage,
|
||||
failure,
|
||||
...(httpStatus === undefined ? {} : { httpStatus }),
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
Implement exact `switch` statements for type/detail normalization and the
|
||||
eight accepted pairs. `readHttpStatus` reads only top-level `code` and accepts
|
||||
integers 400–599. Do not stringify, clone, enumerate, or inspect any message or
|
||||
unknown property.
|
||||
|
||||
- [ ] **Step 5: Run the evidence spec green**
|
||||
|
||||
Run the focused Jest command from Step 1.
|
||||
|
||||
Expected: PASS with the runtime contract and sanitizer cases green.
|
||||
|
||||
- [ ] **Step 6: Commit the evidence boundary**
|
||||
|
||||
```bash
|
||||
git add -- \
|
||||
libs/ui/playback/src/lib/playback-diagnostics/mpegts-playback-evidence.model.ts \
|
||||
libs/ui/playback/src/lib/playback-diagnostics/mpegts-playback-evidence.util.ts \
|
||||
libs/ui/playback/src/lib/playback-diagnostics/mpegts-playback-evidence.util.spec.ts
|
||||
git commit -m "fix(playback): sanitize mpegts error evidence"
|
||||
```
|
||||
|
||||
### Task 2: Replace Heuristic Classification With Structured Evidence
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify:
|
||||
`libs/ui/playback/src/lib/playback-diagnostics/playback-diagnostics.model.ts`
|
||||
- Modify:
|
||||
`libs/ui/playback/src/lib/playback-diagnostics/playback-diagnostics.util.ts`
|
||||
- Modify:
|
||||
`libs/ui/playback/src/lib/playback-diagnostics/playback-error-patterns.util.ts`
|
||||
- Modify:
|
||||
`libs/ui/playback/src/lib/playback-diagnostics/playback-diagnostics.util.spec.ts`
|
||||
- Test:
|
||||
`libs/ui/playback/src/lib/playback-diagnostics/mpegts-playback-evidence.util.spec.ts`
|
||||
|
||||
- [ ] **Step 1: Write failing classifier tests**
|
||||
|
||||
Add table-driven expectations that pass evidence, not raw errors:
|
||||
|
||||
```typescript
|
||||
const issue = classifyMpegTsPlaybackIssue(
|
||||
createMpegTsPlaybackEvidence(
|
||||
'NetworkError',
|
||||
'HttpStatusCodeInvalid',
|
||||
{ code: 404, msg: 'secret' }
|
||||
),
|
||||
createPlaybackSourceMetadata({
|
||||
url: 'https://example.test/missing.ts',
|
||||
mimeType: 'video/mp2t',
|
||||
player: InlinePlaybackPlayer.VideoJs,
|
||||
})
|
||||
);
|
||||
|
||||
expect(issue).toEqual(
|
||||
expect.objectContaining({
|
||||
code: PlaybackDiagnosticCode.NetworkError,
|
||||
source: PlaybackDiagnosticSource.MpegTs,
|
||||
httpStatus: 404,
|
||||
mpegTs: expect.objectContaining({ failure: 'http' }),
|
||||
externalFallbackRecommended: false,
|
||||
})
|
||||
);
|
||||
expect(issue.details).toBeUndefined();
|
||||
```
|
||||
|
||||
Cover timeout/exception → network, early EOF/format/MSE → media decode,
|
||||
unsupported format → container, codec → codec, and inconsistent/other/unknown
|
||||
→ unknown. Verify only decode/container/codec outcomes recommend fallback.
|
||||
|
||||
Run the evidence and diagnostics specs. Expected: FAIL because the classifier
|
||||
still accepts raw `MpegTsPlaybackErrorInput` and emits `details`.
|
||||
|
||||
- [ ] **Step 2: Wire evidence into the shared diagnostic model**
|
||||
|
||||
In `playback-diagnostics.model.ts`:
|
||||
|
||||
```typescript
|
||||
import type { MpegTsPlaybackEvidence } from './mpegts-playback-evidence.model';
|
||||
export * from './mpegts-playback-evidence.model';
|
||||
```
|
||||
|
||||
Remove `MpegTsPlaybackErrorInput` and add:
|
||||
|
||||
```typescript
|
||||
readonly mpegTs?: MpegTsPlaybackEvidence;
|
||||
```
|
||||
|
||||
to `PlaybackDiagnostic`.
|
||||
|
||||
- [ ] **Step 3: Implement evidence-only classification**
|
||||
|
||||
Change the signature to:
|
||||
|
||||
```typescript
|
||||
export function classifyMpegTsPlaybackIssue(
|
||||
evidence: MpegTsPlaybackEvidence,
|
||||
metadata: PlaybackSourceMetadata
|
||||
): PlaybackDiagnostic
|
||||
```
|
||||
|
||||
Map the exact failures from the approved design, pass
|
||||
`httpStatus: evidence.httpStatus` and `mpegTs: evidence` to
|
||||
`createPlaybackDiagnostic`, and add `mpegTs` to that factory's options,
|
||||
destructuring, and result. Export `createMpegTsPlaybackEvidence` from the
|
||||
diagnostics facade.
|
||||
|
||||
- [ ] **Step 4: Remove obsolete mpegts text helpers**
|
||||
|
||||
Delete `normalizeErrorDetails`, `isNetworkFailure`, `isEarlyEofFailure`, and
|
||||
their serialization helpers from `playback-error-patterns.util.ts`. Keep
|
||||
`isBrowserAccessFailure` for native browser errors. Remove other now-unused
|
||||
text helpers only when `rg` proves they have no callers.
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
rg -n "normalizeErrorDetails|isNetworkFailure|isEarlyEofFailure|MpegTsPlaybackErrorInput" \
|
||||
libs/ui/playback/src/lib
|
||||
```
|
||||
|
||||
Expected: no matches.
|
||||
|
||||
- [ ] **Step 5: Run classifier tests green**
|
||||
|
||||
Run both focused specs by path.
|
||||
|
||||
Expected: PASS with no raw message or arbitrary payload retained.
|
||||
|
||||
- [ ] **Step 6: Commit structured classification**
|
||||
|
||||
```bash
|
||||
git add -- \
|
||||
libs/ui/playback/src/lib/playback-diagnostics/playback-diagnostics.model.ts \
|
||||
libs/ui/playback/src/lib/playback-diagnostics/playback-diagnostics.util.ts \
|
||||
libs/ui/playback/src/lib/playback-diagnostics/playback-error-patterns.util.ts \
|
||||
libs/ui/playback/src/lib/playback-diagnostics/playback-diagnostics.util.spec.ts \
|
||||
libs/ui/playback/src/lib/playback-diagnostics/mpegts-playback-evidence.util.spec.ts
|
||||
git commit -m "fix(playback): classify exact mpegts failures"
|
||||
```
|
||||
|
||||
### Task 3: Route HTML5 Through The Shared Boundary
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify:
|
||||
`libs/ui/playback/src/lib/html-video-player/html-video-player-diagnostics.ts`
|
||||
- Modify:
|
||||
`libs/ui/playback/src/lib/html-video-player/html-video-player.component.shared-controls.spec-fixtures.ts`
|
||||
- Modify:
|
||||
`libs/ui/playback/src/lib/html-video-player/html-video-player.component.shared-controls.sources.spec.ts`
|
||||
|
||||
- [ ] **Step 1: Make the mpegts fixture emit registered events**
|
||||
|
||||
Give `MockMpegTsPlayer` a listener map and `emit(event, ...args)` helper, matching
|
||||
the existing VJS and ArtPlayer test doubles. Preserve lifecycle logging for
|
||||
`on`, `off`, attachment, and load.
|
||||
|
||||
- [ ] **Step 2: Write the failing HTML5 HTTP regression**
|
||||
|
||||
Start a `.ts` source, emit:
|
||||
|
||||
```typescript
|
||||
engine.emit('error', 'NetworkError', 'HttpStatusCodeInvalid', {
|
||||
code: 404,
|
||||
msg: 'Not Found html-secret',
|
||||
url: 'https://provider.example/error?token=html-secret',
|
||||
});
|
||||
```
|
||||
|
||||
Assert the component emits HTML5 source metadata, `network-error`, HTTP 404,
|
||||
structured `mpegTs` evidence, no raw `details`, and no external fallback.
|
||||
|
||||
Run the focused sources spec. Expected: FAIL because the HTML5 helper still
|
||||
passes a raw error object to the classifier.
|
||||
|
||||
- [ ] **Step 3: Normalize before classification**
|
||||
|
||||
Import `createMpegTsPlaybackEvidence` in
|
||||
`html-video-player-diagnostics.ts` and change:
|
||||
|
||||
```typescript
|
||||
classifyMpegTsPlaybackIssue(
|
||||
createMpegTsPlaybackEvidence(error.type, error.details, error.info),
|
||||
createHtml5SourceMetadata(url, 'video/mp2t')
|
||||
)
|
||||
```
|
||||
|
||||
Accept unknown callback values so normalization owns validation.
|
||||
|
||||
- [ ] **Step 4: Run the HTML5 regression green and commit**
|
||||
|
||||
Run the focused sources spec, then:
|
||||
|
||||
```bash
|
||||
git add -- \
|
||||
libs/ui/playback/src/lib/html-video-player/html-video-player-diagnostics.ts \
|
||||
libs/ui/playback/src/lib/html-video-player/html-video-player.component.shared-controls.spec-fixtures.ts \
|
||||
libs/ui/playback/src/lib/html-video-player/html-video-player.component.shared-controls.sources.spec.ts
|
||||
git commit -m "fix(playback): structure HTML5 mpegts errors"
|
||||
```
|
||||
|
||||
### Task 4: Route Video.js And ArtPlayer Through The Same Boundary
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify: `libs/ui/playback/src/lib/vjs-player/vjs-mpegts-session.ts`
|
||||
- Modify: `libs/ui/playback/src/lib/vjs-player/vjs-mpegts-session.spec.ts`
|
||||
- Modify: `libs/ui/playback/src/lib/art-player/art-player-source-session.ts`
|
||||
- Modify: `libs/ui/playback/src/lib/art-player/art-player-source-session.spec.ts`
|
||||
- Modify: `libs/ui/playback/src/lib/art-player/art-player.component.spec.ts`
|
||||
|
||||
- [ ] **Step 1: Replace the misleading VJS CORS regression**
|
||||
|
||||
Change the existing synthetic `FetchError`/CORS case to the real public event:
|
||||
|
||||
```typescript
|
||||
mpegTsPlayer.emit(
|
||||
'error',
|
||||
'NetworkError',
|
||||
'HttpStatusCodeInvalid',
|
||||
{ code: 503, msg: 'provider secret', headers: { Authorization: 'secret' } }
|
||||
);
|
||||
```
|
||||
|
||||
Expect `network-error`, HTTP 503, `player=videojs`, exact mpegts evidence, no
|
||||
fallback, and serialized output without either secret. Add a generic
|
||||
`NetworkError + Exception` case proving message text that says CORS remains a
|
||||
generic network diagnostic.
|
||||
|
||||
Run the VJS session spec. Expected: FAIL under the raw classifier contract.
|
||||
|
||||
- [ ] **Step 2: Normalize in VjsMpegTsSession**
|
||||
|
||||
Import the factory and call:
|
||||
|
||||
```typescript
|
||||
classifyMpegTsPlaybackIssue(
|
||||
createMpegTsPlaybackEvidence(type, details, info),
|
||||
createPlaybackSourceMetadata({
|
||||
url,
|
||||
mimeType: 'video/mp2t',
|
||||
player: InlinePlaybackPlayer.VideoJs,
|
||||
})
|
||||
)
|
||||
```
|
||||
|
||||
Keep duration sync, listener cleanup, play, and teardown unchanged.
|
||||
|
||||
- [ ] **Step 3: Add the failing ArtPlayer structured regression**
|
||||
|
||||
Emit `MediaError + CodecUnsupported` with a secret-bearing `info` object.
|
||||
Assert `unsupported-codec`, `player=artplayer`, structured evidence, fallback
|
||||
enabled, and no secret in the emitted issue. Update old lowercase/message-based
|
||||
fixtures to exact public constants.
|
||||
|
||||
Run the ArtPlayer source-session and component specs. Expected: FAIL until the
|
||||
session uses the factory.
|
||||
|
||||
- [ ] **Step 4: Normalize in ArtPlayerSourceSession**
|
||||
|
||||
Import `createMpegTsPlaybackEvidence` and pass the callback arguments through
|
||||
it before the shared classifier. Change the stored listener parameter types to
|
||||
`unknown` so the sanitizer, not the adapter, owns validation. Preserve the
|
||||
destroyed/current-engine guard.
|
||||
|
||||
- [ ] **Step 5: Run all three engine-owner specs green and commit**
|
||||
|
||||
Run HTML5 sources, VJS session, ArtPlayer source-session, and ArtPlayer
|
||||
component specs by path. Then:
|
||||
|
||||
```bash
|
||||
git add -- \
|
||||
libs/ui/playback/src/lib/vjs-player/vjs-mpegts-session.ts \
|
||||
libs/ui/playback/src/lib/vjs-player/vjs-mpegts-session.spec.ts \
|
||||
libs/ui/playback/src/lib/art-player/art-player-source-session.ts \
|
||||
libs/ui/playback/src/lib/art-player/art-player-source-session.spec.ts \
|
||||
libs/ui/playback/src/lib/art-player/art-player.component.spec.ts
|
||||
git commit -m "fix(playback): share mpegts evidence across players"
|
||||
```
|
||||
|
||||
### Task 5: Render Only Structured mpegts.js Details
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify:
|
||||
`libs/ui/playback/src/lib/web-player-view/web-player-view-diagnostics.utils.ts`
|
||||
- Modify:
|
||||
`libs/ui/playback/src/lib/web-player-view/web-player-view.component.spec.ts`
|
||||
|
||||
- [ ] **Step 1: Write the failing rendering and privacy regression**
|
||||
|
||||
Add `createStructuredMpegTsDiagnostic()` with structured HTTP 404 evidence plus
|
||||
legacy `details` and native message fields containing sentinels. Assert:
|
||||
|
||||
```typescript
|
||||
expect(component.getDiagnosticDetails(issue)).toEqual(
|
||||
expect.arrayContaining([
|
||||
{
|
||||
labelKey: 'PLAYBACK_DIAGNOSTICS.DETAIL_ERROR_DETAILS',
|
||||
value:
|
||||
'stage=loader · failure=http · type=NetworkError · ' +
|
||||
'details=HttpStatusCodeInvalid · disposition=terminal · HTTP 404',
|
||||
},
|
||||
])
|
||||
);
|
||||
expect(renderedDetails).not.toContain('mpegts-render-secret');
|
||||
expect(component.getDiagnosticMeta(issue)).toBe('HTTP 404');
|
||||
```
|
||||
|
||||
Run the component spec. Expected: FAIL because mpegts evidence is not formatted.
|
||||
|
||||
- [ ] **Step 2: Add the structured formatter branch**
|
||||
|
||||
Before VHS/HLS legacy formatting, add:
|
||||
|
||||
```typescript
|
||||
if (issue.mpegTs) {
|
||||
return [
|
||||
`stage=${issue.mpegTs.stage}`,
|
||||
`failure=${issue.mpegTs.failure}`,
|
||||
`type=${issue.mpegTs.engineType}`,
|
||||
`details=${issue.mpegTs.engineDetails}`,
|
||||
`disposition=${issue.mpegTs.disposition}`,
|
||||
issue.mpegTs.httpStatus === undefined
|
||||
? ''
|
||||
: `HTTP ${issue.mpegTs.httpStatus}`,
|
||||
]
|
||||
.filter((value) => value.length > 0)
|
||||
.join(' · ');
|
||||
}
|
||||
```
|
||||
|
||||
Also suppress the native-message row when `issue.mpegTs` exists, matching the
|
||||
HLS/VHS/Shaka structured boundaries.
|
||||
|
||||
- [ ] **Step 3: Run the view regression green and commit**
|
||||
|
||||
```bash
|
||||
git add -- \
|
||||
libs/ui/playback/src/lib/web-player-view/web-player-view-diagnostics.utils.ts \
|
||||
libs/ui/playback/src/lib/web-player-view/web-player-view.component.spec.ts
|
||||
git commit -m "fix(playback): render structured mpegts evidence"
|
||||
```
|
||||
|
||||
### Task 6: Update Canonical Documentation And Release Note
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify: `docs/architecture/embedded-inline-playback.md`
|
||||
- Modify: `AGENTS.md`
|
||||
- Modify: `CLAUDE.md`
|
||||
- Create: `.changes/playback-structured-mpegts-diagnostics.md`
|
||||
|
||||
- [ ] **Step 1: Update the canonical diagnostic contract**
|
||||
|
||||
Replace the current early-EOF-only mpegts paragraph with the 1.8.0 public
|
||||
boundary: exact type/detail pairs, terminal player events, validated HTTP
|
||||
status, classification mapping, rejected raw fields, all three owners, and the
|
||||
absence of message-based browser-access guesses.
|
||||
|
||||
- [ ] **Step 2: Keep repository guidance current**
|
||||
|
||||
Add the same concise contract to the mpegts ownership summaries in `AGENTS.md`
|
||||
and `CLAUDE.md`: all three owners use the shared version-locked evidence
|
||||
boundary; raw payloads do not cross it; shared controls remain unchanged.
|
||||
|
||||
- [ ] **Step 3: Add the user-facing release note**
|
||||
|
||||
Create:
|
||||
|
||||
```markdown
|
||||
---
|
||||
type: fix
|
||||
area: playback
|
||||
issues: [1159]
|
||||
---
|
||||
|
||||
MPEG-TS playback errors now show exact engine evidence, including HTTP status,
|
||||
without exposing provider response details. Format, codec, truncated-stream,
|
||||
and MediaSource failures now produce more accurate fallback guidance.
|
||||
```
|
||||
|
||||
Keep the body below 400 characters.
|
||||
|
||||
- [ ] **Step 4: Validate docs and note, then commit**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
git diff --check
|
||||
pnpm run release:notes:validate
|
||||
```
|
||||
|
||||
Expected: exit 0. Then:
|
||||
|
||||
```bash
|
||||
git add -- \
|
||||
docs/architecture/embedded-inline-playback.md \
|
||||
AGENTS.md CLAUDE.md \
|
||||
.changes/playback-structured-mpegts-diagnostics.md
|
||||
git commit -m "docs(playback): document structured mpegts diagnostics"
|
||||
```
|
||||
|
||||
### Task 7: Complete Validation And Local P1/P2 Review
|
||||
|
||||
**Files:**
|
||||
|
||||
- Verify all files changed since `origin/master`
|
||||
|
||||
- [ ] **Step 1: Run focused regression tests**
|
||||
|
||||
Run all new and changed spec files by path with the web ESM Jest config.
|
||||
|
||||
Expected: every focused suite passes.
|
||||
|
||||
- [ ] **Step 2: Run the affected validation ladder**
|
||||
|
||||
```bash
|
||||
pnpm nx test ui-playback
|
||||
pnpm nx lint ui-playback
|
||||
pnpm run typecheck:web
|
||||
pnpm run i18n:check
|
||||
pnpm run release:notes:validate
|
||||
```
|
||||
|
||||
Expected: all commands exit 0. No new E2E is required because source routing,
|
||||
player lifecycle, actions, and UI layout do not change; engine events, all
|
||||
three adapters, classification, privacy, and rendered output have deterministic
|
||||
unit/integration coverage.
|
||||
|
||||
- [ ] **Step 3: Inspect size, scope, and accidental raw-data retention**
|
||||
|
||||
```bash
|
||||
git diff --check origin/master...HEAD
|
||||
git diff --stat origin/master...HEAD
|
||||
rg -n "JSON\.stringify|info\.msg|error\.message|normalizeErrorDetails|isNetworkFailure|isEarlyEofFailure" \
|
||||
libs/ui/playback/src/lib/playback-diagnostics \
|
||||
libs/ui/playback/src/lib/html-video-player/html-video-player-diagnostics.ts \
|
||||
libs/ui/playback/src/lib/vjs-player/vjs-mpegts-session.ts \
|
||||
libs/ui/playback/src/lib/art-player/art-player-source-session.ts
|
||||
```
|
||||
|
||||
Expected: no mpegts error serialization/message inference; any unrelated
|
||||
matches are inspected and justified.
|
||||
|
||||
- [ ] **Step 4: Perform the requested local Codex review**
|
||||
|
||||
Review `origin/master...HEAD` specifically for P1/P2 defects:
|
||||
|
||||
- false classification from mismatched type/detail pairs;
|
||||
- unvalidated or duplicated HTTP status;
|
||||
- raw provider data escaping through top-level or rendered fields;
|
||||
- different behavior among HTML5, Video.js, and ArtPlayer;
|
||||
- missing stale-listener/teardown guards;
|
||||
- incorrect fallback behavior;
|
||||
- version-lock drift;
|
||||
- missing tests or stale canonical docs.
|
||||
|
||||
Fix every confirmed P1/P2 finding through a new failing regression first, rerun
|
||||
the focused and affected validation ladders, and commit the fixes. Repeat until
|
||||
the review is clean.
|
||||
|
||||
- [ ] **Step 5: Verify the final branch state**
|
||||
|
||||
```bash
|
||||
git status --short --branch
|
||||
git log --oneline origin/master..HEAD
|
||||
git diff --check origin/master...HEAD
|
||||
```
|
||||
|
||||
Expected: clean worktree, focused commits only, and no whitespace errors.
|
||||
|
||||
### Task 8: Publish The Pull Request After Explicit Authorization
|
||||
|
||||
**Files:**
|
||||
|
||||
- Inspect: all changes in `origin/master...HEAD`
|
||||
|
||||
- [ ] **Step 1: Confirm final staged and branch scope**
|
||||
|
||||
Do not stage unrelated files. Verify the exact branch and commits before push.
|
||||
|
||||
- [ ] **Step 2: Push only after user authorization**
|
||||
|
||||
```bash
|
||||
git push -u origin agent/structure-mpegts-diagnostics
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Create one ready PR only after user authorization**
|
||||
|
||||
Use base `master`, head `agent/structure-mpegts-diagnostics`, and summarize:
|
||||
|
||||
- version-locked public mpegts.js evidence;
|
||||
- exact HTTP/format/codec/EOF/MSE classification;
|
||||
- shared HTML5, Video.js, and ArtPlayer integration;
|
||||
- privacy boundary and future recommendation-layer compatibility;
|
||||
- tests and validation run.
|
||||
|
||||
Link issue #1159 and include the release note. Do not create multiple or draft
|
||||
PRs unless the user requests that state.
|
||||
Reference in new issue
Block a user