mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
refactor(spf): extract text-loader vocabulary to primitives behind a CueSink seam
Mirror of the v/a extraction for the text side. The text step vocabulary (TextFrame/TextLoadStep/TextStepDeps/TextMessagePipelines/TextLoadTask/ TextTrackSegmentResolver + resolveCues/dispatchCues) and the cue-sink message DTOs (AddCuesMessage/CueSegmentMeta) move out of the text loader / text-tracks actor into primitives/. All DOM-free (generic over Cue), so they land in primitives/ root. The dispatch step reaches its sink through a structural CueSink seam instead of the concrete TextTracksActor, so the pipeline names no actor; the loader keeps only scheduling. actors/ (root) and engines/hls gain primitives references. Net effect: relocation-pipelines now imports only core/media/primitives — zero actors/behaviors. It's ready to move to primitives; only the DOM VTTCue in its text step keeps it in behaviors/dom for now (slice 3). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
a09473a372
commit
0b8efb34bc
@@ -1,5 +1,4 @@
|
||||
import { createMachineActor, type HandlerContext, type MessageActor } from '../../core/actors/create-machine-actor';
|
||||
import type { AnySlotMap } from '../../core/composition/create-composition';
|
||||
import { peek } from '../../core/signals/primitives';
|
||||
import { SerialRunner, Task } from '../../core/tasks/task';
|
||||
import {
|
||||
@@ -7,7 +6,15 @@ import {
|
||||
type ForwardBufferConfig,
|
||||
getSegmentsToLoad,
|
||||
} from '../../media/buffer/forward-buffer';
|
||||
import type { Cue, Segment, TextTrack } from '../../media/types';
|
||||
import type { Cue, TextTrack } from '../../media/types';
|
||||
import {
|
||||
DEFAULT_TEXT_MESSAGE_PIPELINES,
|
||||
type TextFrame,
|
||||
type TextLoadTask,
|
||||
type TextMessagePipelines,
|
||||
type TextStepDeps,
|
||||
type TextTrackSegmentResolver,
|
||||
} from '../primitives/text-segment-load-pipeline';
|
||||
import type { TextTracksActor } from './text-tracks';
|
||||
|
||||
// =============================================================================
|
||||
@@ -52,81 +59,6 @@ export type TextTrackSegmentLoaderActor = MessageActor<
|
||||
TextTrackSegmentLoaderMessage
|
||||
>;
|
||||
|
||||
/**
|
||||
* Resolves a text-track segment URL into the array of cues it contains.
|
||||
*
|
||||
* "Resolve" because the fn covers both network fetch and parse into the
|
||||
* domain model. Host-agnostic — the concrete resolver (e.g. the
|
||||
* browser's native VTT resolver) is supplied at engine-assembly time,
|
||||
* so this actor stays DOM-free. A pure `url → cues` primitive (the text
|
||||
* analog of the v/a loader's `fetchBytes`); composition-awareness lives in
|
||||
* the injected {@link TextLoadStep}s, not here.
|
||||
*/
|
||||
export type TextTrackSegmentResolver<C extends Cue = Cue> = (url: string) => Promise<C[]>;
|
||||
|
||||
/**
|
||||
* A text load in mid-pipeline — the text analog of the v/a loader's `Frame`.
|
||||
* `resolveCuesStep` fills `cues`; `dispatchCuesStep` sends them. `metadata` is
|
||||
* opaque header metadata a resolve step may attach for a later step to read
|
||||
* (e.g. relocation stashes the `X-TIMESTAMP-MAP` correlation here for its rebase
|
||||
* step). Typed `unknown` so the generic loader stays host-agnostic — the step
|
||||
* that reads it knows its concrete shape (mirrors `StepDeps.state`).
|
||||
*/
|
||||
export interface TextFrame<C extends Cue = Cue> {
|
||||
readonly op: TextLoadTask;
|
||||
cues?: C[];
|
||||
metadata?: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* One stage of a text message pipeline — the text analog of the v/a loader's
|
||||
* `LoadStep`. Mutates the {@link TextFrame} in place and may be async; the runner
|
||||
* checks `signal.aborted` before each step and passes the actor's
|
||||
* {@link TextStepDeps} on every call, so a stateless step (`resolveCuesStep`) is a
|
||||
* plain value and a step that needs composition signals (relocation's cue rebase)
|
||||
* reads them from `deps` at call time.
|
||||
*/
|
||||
export type TextLoadStep<C extends Cue = Cue> = (
|
||||
frame: TextFrame<C>,
|
||||
signal: AbortSignal,
|
||||
deps: TextStepDeps
|
||||
) => void | Promise<void>;
|
||||
|
||||
/**
|
||||
* The uniform passthrough handed to each {@link TextLoadStep} — the composition triple,
|
||||
* the text analog of `StepDeps`. `state`/`context` are the composition signal maps;
|
||||
* `config` is the threaded config with the loader's wiring folded in (see
|
||||
* {@link textStepWiring} + `createTextTrackSegmentLoaderActor`). Typed loose: composition
|
||||
* steps read `state`; base steps read the folded wiring off `config`.
|
||||
*/
|
||||
export interface TextStepDeps {
|
||||
state: AnySlotMap;
|
||||
context: AnySlotMap;
|
||||
config: object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base-step view of the loader's wiring, folded into `config` by
|
||||
* `createTextTrackSegmentLoaderActor` so base steps read it from the uniform passthrough
|
||||
* — present in both composition and standalone use. `config` is loose (`object`), so
|
||||
* assert the shape here (mirrors the v/a loader's `stepWiring`).
|
||||
*/
|
||||
export function textStepWiring<C extends Cue>(
|
||||
deps: TextStepDeps
|
||||
): { textTracksActor: TextTracksActor<C>; resolveSegment: TextTrackSegmentResolver<C> } {
|
||||
return deps.config as { textTracksActor: TextTracksActor<C>; resolveSegment: TextTrackSegmentResolver<C> };
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the ordered step list, called **once per actor** (mirrors the v/a loader's
|
||||
* `MessagePipelines`, but text has a single op type so it's a flat array, not a
|
||||
* `Record`). The default ({@link DEFAULT_TEXT_MESSAGE_PIPELINES}) is
|
||||
* `resolveCues → dispatchCues`; a non-zero-PTS composition returns a list that
|
||||
* inserts a cue-rebase step (see `relocatingTextPipelines`), so the loader stays
|
||||
* oblivious to relocation.
|
||||
*/
|
||||
export type TextMessagePipelines<C extends Cue = Cue> = () => TextLoadStep<C>[];
|
||||
|
||||
/**
|
||||
* Configuration for `createTextTrackSegmentLoaderActor`. Spread over
|
||||
* `DEFAULT_FORWARD_BUFFER_CONFIG` to override individual forward-window
|
||||
@@ -144,54 +76,6 @@ export interface TextTrackSegmentLoaderActorConfig<C extends Cue = Cue> {
|
||||
// Implementation
|
||||
// =============================================================================
|
||||
|
||||
/** Internal load-task descriptor — one segment fetch + dispatch unit. */
|
||||
interface TextLoadTask {
|
||||
segment: Segment;
|
||||
trackId: string;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Steps
|
||||
// =============================================================================
|
||||
|
||||
// Base steps are generic over the cue type `C` (generic arrow consts, not
|
||||
// `TextLoadStep<Cue>` values): each touches `C` — `frame.cues: C[]` and the
|
||||
// `C`-typed `textStepWiring<C>` — so a `Cue`-typed const wouldn't slot into a
|
||||
// `VTTCue` pipeline. A generic function assigns to any `TextLoadStep<C>`.
|
||||
|
||||
/** Resolve the op's cues (via the injected host primitive) into the frame. The text analog of `fetchStep`. */
|
||||
export const resolveCuesStep = async <C extends Cue>(
|
||||
frame: TextFrame<C>,
|
||||
signal: AbortSignal,
|
||||
deps: TextStepDeps
|
||||
): Promise<void> => {
|
||||
const cues = await textStepWiring<C>(deps).resolveSegment(frame.op.segment.url);
|
||||
if (signal.aborted) return;
|
||||
frame.cues = cues;
|
||||
};
|
||||
|
||||
/** Dispatch the frame's cues to the TextTracksActor as `add-cues`. The text analog of `dispatchStep`. */
|
||||
export const dispatchCuesStep = <C extends Cue>(
|
||||
frame: TextFrame<C>,
|
||||
_signal: AbortSignal,
|
||||
deps: TextStepDeps
|
||||
): void => {
|
||||
const { op } = frame;
|
||||
textStepWiring<C>(deps).textTracksActor.send({
|
||||
type: 'add-cues',
|
||||
meta: {
|
||||
trackId: op.trackId,
|
||||
id: op.segment.id,
|
||||
startTime: op.segment.startTime,
|
||||
duration: op.segment.duration,
|
||||
},
|
||||
cues: frame.cues ?? [],
|
||||
});
|
||||
};
|
||||
|
||||
/** Tier 0 default: resolve then dispatch. No relocation vocabulary. */
|
||||
const DEFAULT_TEXT_MESSAGE_PIPELINES = <C extends Cue>(): TextLoadStep<C>[] => [resolveCuesStep, dispatchCuesStep];
|
||||
|
||||
/**
|
||||
* Loads text-track segments for a track and delegates cue management
|
||||
* to a TextTracksActor. Mirrors the v/a `SegmentLoaderActor` shape (FSM
|
||||
|
||||
Reference in New Issue
Block a user