@videojs/spf 10.0.0-beta.14 → 10.0.0-beta.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/default/core/buffer/forward-buffer.js.map +1 -1
- package/dist/default/core/create-machine-actor.js +91 -0
- package/dist/default/core/create-machine-actor.js.map +1 -0
- package/dist/default/core/create-machine-reactor.js +83 -0
- package/dist/default/core/create-machine-reactor.js.map +1 -0
- package/dist/default/core/create-transition-actor.js +46 -0
- package/dist/default/core/create-transition-actor.js.map +1 -0
- package/dist/default/core/features/resolve-presentation.js +42 -46
- package/dist/default/core/features/resolve-presentation.js.map +1 -1
- package/dist/default/core/features/resolve-track.js +1 -1
- package/dist/default/core/machine.js +26 -0
- package/dist/default/core/machine.js.map +1 -0
- package/dist/default/core/signals/primitives.js +1 -1
- package/dist/default/core/task.js +76 -7
- package/dist/default/core/task.js.map +1 -1
- package/dist/default/core/types/index.js.map +1 -1
- package/dist/default/dom/features/end-of-stream.js +5 -5
- package/dist/default/dom/features/end-of-stream.js.map +1 -1
- package/dist/default/dom/features/load-text-track-cues.js +80 -137
- package/dist/default/dom/features/load-text-track-cues.js.map +1 -1
- package/dist/default/dom/features/segment-loader-actor.js +150 -103
- package/dist/default/dom/features/segment-loader-actor.js.map +1 -1
- package/dist/default/dom/features/sync-text-tracks.js +95 -0
- package/dist/default/dom/features/sync-text-tracks.js.map +1 -0
- package/dist/default/dom/features/text-track-segment-loader-actor.js +55 -0
- package/dist/default/dom/features/text-track-segment-loader-actor.js.map +1 -0
- package/dist/default/dom/features/text-tracks-actor.js +42 -0
- package/dist/default/dom/features/text-tracks-actor.js.map +1 -0
- package/dist/default/dom/features/track-playback-initiated.js +51 -38
- package/dist/default/dom/features/track-playback-initiated.js.map +1 -1
- package/dist/default/dom/media/source-buffer-actor.js +52 -91
- package/dist/default/dom/media/source-buffer-actor.js.map +1 -1
- package/dist/default/dom/network/fetch.js +0 -1
- package/dist/default/dom/network/fetch.js.map +1 -1
- package/dist/default/dom/playback-engine/engine.js +4 -13
- package/dist/default/dom/playback-engine/engine.js.map +1 -1
- package/dist/dev/core/actor.d.ts +19 -10
- package/dist/dev/core/actor.d.ts.map +1 -1
- package/dist/dev/core/buffer/forward-buffer.js.map +1 -1
- package/dist/dev/core/create-machine-actor.d.ts +12 -0
- package/dist/dev/core/create-machine-actor.d.ts.map +1 -0
- package/dist/dev/core/create-machine-actor.js +91 -0
- package/dist/dev/core/create-machine-actor.js.map +1 -0
- package/dist/dev/core/create-machine-reactor.d.ts +8 -0
- package/dist/dev/core/create-machine-reactor.d.ts.map +1 -0
- package/dist/dev/core/create-machine-reactor.js +83 -0
- package/dist/dev/core/create-machine-reactor.js.map +1 -0
- package/dist/dev/core/create-transition-actor.d.ts +23 -0
- package/dist/dev/core/create-transition-actor.d.ts.map +1 -0
- package/dist/dev/core/create-transition-actor.js +46 -0
- package/dist/dev/core/create-transition-actor.js.map +1 -0
- package/dist/dev/core/features/resolve-presentation.js +42 -46
- package/dist/dev/core/features/resolve-presentation.js.map +1 -1
- package/dist/dev/core/features/resolve-track.js +1 -1
- package/dist/dev/core/machine.d.ts +21 -0
- package/dist/dev/core/machine.d.ts.map +1 -0
- package/dist/dev/core/machine.js +26 -0
- package/dist/dev/core/machine.js.map +1 -0
- package/dist/dev/core/signals/primitives.js +1 -1
- package/dist/dev/core/task.js +76 -7
- package/dist/dev/core/task.js.map +1 -1
- package/dist/dev/core/types/index.d.ts +1 -8
- package/dist/dev/core/types/index.d.ts.map +1 -1
- package/dist/dev/core/types/index.js.map +1 -1
- package/dist/dev/dom/features/end-of-stream.js +5 -5
- package/dist/dev/dom/features/end-of-stream.js.map +1 -1
- package/dist/dev/dom/features/load-text-track-cues.d.ts +50 -26
- package/dist/dev/dom/features/load-text-track-cues.d.ts.map +1 -1
- package/dist/dev/dom/features/load-text-track-cues.js +80 -137
- package/dist/dev/dom/features/load-text-track-cues.js.map +1 -1
- package/dist/dev/dom/features/segment-loader-actor.js +150 -103
- package/dist/dev/dom/features/segment-loader-actor.js.map +1 -1
- package/dist/dev/dom/features/sync-text-tracks.js +95 -0
- package/dist/dev/dom/features/sync-text-tracks.js.map +1 -0
- package/dist/dev/dom/features/text-track-segment-loader-actor.d.ts +12 -0
- package/dist/dev/dom/features/text-track-segment-loader-actor.d.ts.map +1 -0
- package/dist/dev/dom/features/text-track-segment-loader-actor.js +55 -0
- package/dist/dev/dom/features/text-track-segment-loader-actor.js.map +1 -0
- package/dist/dev/dom/features/text-tracks-actor.d.ts +31 -0
- package/dist/dev/dom/features/text-tracks-actor.d.ts.map +1 -0
- package/dist/dev/dom/features/text-tracks-actor.js +42 -0
- package/dist/dev/dom/features/text-tracks-actor.js.map +1 -0
- package/dist/dev/dom/features/track-playback-initiated.d.ts +11 -9
- package/dist/dev/dom/features/track-playback-initiated.d.ts.map +1 -1
- package/dist/dev/dom/features/track-playback-initiated.js +51 -38
- package/dist/dev/dom/features/track-playback-initiated.js.map +1 -1
- package/dist/dev/dom/media/source-buffer-actor.d.ts +14 -8
- package/dist/dev/dom/media/source-buffer-actor.d.ts.map +1 -1
- package/dist/dev/dom/media/source-buffer-actor.js +52 -91
- package/dist/dev/dom/media/source-buffer-actor.js.map +1 -1
- package/dist/dev/dom/network/fetch.js +0 -1
- package/dist/dev/dom/network/fetch.js.map +1 -1
- package/dist/dev/dom/playback-engine/engine.d.ts +4 -3
- package/dist/dev/dom/playback-engine/engine.d.ts.map +1 -1
- package/dist/dev/dom/playback-engine/engine.js +4 -13
- package/dist/dev/dom/playback-engine/engine.js.map +1 -1
- package/package.json +5 -4
- package/dist/default/dom/features/setup-text-tracks.js +0 -67
- package/dist/default/dom/features/setup-text-tracks.js.map +0 -1
- package/dist/default/dom/features/sync-selected-text-track-from-dom.js +0 -54
- package/dist/default/dom/features/sync-selected-text-track-from-dom.js.map +0 -1
- package/dist/default/dom/features/sync-text-track-modes.js +0 -33
- package/dist/default/dom/features/sync-text-track-modes.js.map +0 -1
- package/dist/dev/dom/features/setup-text-tracks.js +0 -67
- package/dist/dev/dom/features/setup-text-tracks.js.map +0 -1
- package/dist/dev/dom/features/sync-selected-text-track-from-dom.js +0 -54
- package/dist/dev/dom/features/sync-selected-text-track-from-dom.js.map +0 -1
- package/dist/dev/dom/features/sync-text-track-modes.js +0 -33
- package/dist/dev/dom/features/sync-text-track-modes.js.map +0 -1
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { effect } from "../../core/signals/effect.js";
|
|
2
|
-
import { computed, update } from "../../core/signals/primitives.js";
|
|
3
|
-
import { listen } from "@videojs/utils/dom";
|
|
4
|
-
//#region src/dom/features/sync-selected-text-track-from-dom.ts
|
|
5
|
-
/**
|
|
6
|
-
* Sync selectedTextTrackId from DOM text track mode changes.
|
|
7
|
-
*
|
|
8
|
-
* Listens to the `change` event on `media.textTracks` and updates
|
|
9
|
-
* `selectedTextTrackId` when external code (e.g. the captions button via
|
|
10
|
-
* `toggleSubtitles()`) changes a subtitle/caption track mode to 'showing'.
|
|
11
|
-
*
|
|
12
|
-
* This bridges the core store's `toggleSubtitles()` with SPF's reactive text
|
|
13
|
-
* track pipeline (`syncTextTrackModes`, `loadTextTrackCues`). Without this
|
|
14
|
-
* bridge, direct DOM mode changes would be immediately overridden by
|
|
15
|
-
* `syncTextTrackModes` on the next SPF state update.
|
|
16
|
-
*
|
|
17
|
-
* When a subtitle/caption track's mode is 'showing', its DOM `id` — which
|
|
18
|
-
* matches the SPF track ID set by `setupTextTracks` — is written to
|
|
19
|
-
* `selectedTextTrackId`. When no subtitle/caption track is 'showing',
|
|
20
|
-
* `selectedTextTrackId` is cleared along with the deselected track's
|
|
21
|
-
* `textBufferState` entry — setting mode to 'disabled' clears native cues from
|
|
22
|
-
* the track element, so the buffer must be reset to re-fetch cues on re-enable.
|
|
23
|
-
*
|
|
24
|
-
* @example
|
|
25
|
-
* const cleanup = syncSelectedTextTrackFromDom({ state, owners });
|
|
26
|
-
*/
|
|
27
|
-
function syncSelectedTextTrackFromDom({ state, owners }) {
|
|
28
|
-
const mediaElement = computed(() => owners.get().mediaElement);
|
|
29
|
-
return effect(() => {
|
|
30
|
-
const el = mediaElement.get();
|
|
31
|
-
if (!el) return;
|
|
32
|
-
return listen(el.textTracks, "change", () => {
|
|
33
|
-
const newId = Array.from(el.textTracks).find((t) => t.mode === "showing" && (t.kind === "subtitles" || t.kind === "captions"))?.id || void 0;
|
|
34
|
-
const current = state.get();
|
|
35
|
-
if (current.selectedTextTrackId === newId) return;
|
|
36
|
-
if (newId) update(state, { selectedTextTrackId: newId });
|
|
37
|
-
else {
|
|
38
|
-
const prevId = current.selectedTextTrackId;
|
|
39
|
-
if (prevId && current.textBufferState?.[prevId]) {
|
|
40
|
-
const next = { ...current.textBufferState };
|
|
41
|
-
delete next[prevId];
|
|
42
|
-
update(state, {
|
|
43
|
-
selectedTextTrackId: void 0,
|
|
44
|
-
textBufferState: next
|
|
45
|
-
});
|
|
46
|
-
} else update(state, { selectedTextTrackId: void 0 });
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
//#endregion
|
|
52
|
-
export { syncSelectedTextTrackFromDom };
|
|
53
|
-
|
|
54
|
-
//# sourceMappingURL=sync-selected-text-track-from-dom.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sync-selected-text-track-from-dom.js","names":[],"sources":["../../../../src/dom/features/sync-selected-text-track-from-dom.ts"],"sourcesContent":["import { listen } from '@videojs/utils/dom';\nimport { effect } from '../../core/signals/effect';\nimport { computed, type Signal, update } from '../../core/signals/primitives';\nimport type { TextTrackBufferState } from './load-text-track-cues';\n\n/**\n * State shape for DOM-driven text track selection.\n */\nexport interface SelectedTextTrackFromDomState {\n selectedTextTrackId?: string | undefined;\n textBufferState?: TextTrackBufferState | undefined;\n}\n\n/**\n * Owners shape for DOM-driven text track selection.\n */\nexport interface SelectedTextTrackFromDomOwners {\n mediaElement?: HTMLMediaElement | undefined;\n}\n\n/**\n * Sync selectedTextTrackId from DOM text track mode changes.\n *\n * Listens to the `change` event on `media.textTracks` and updates\n * `selectedTextTrackId` when external code (e.g. the captions button via\n * `toggleSubtitles()`) changes a subtitle/caption track mode to 'showing'.\n *\n * This bridges the core store's `toggleSubtitles()` with SPF's reactive text\n * track pipeline (`syncTextTrackModes`, `loadTextTrackCues`). Without this\n * bridge, direct DOM mode changes would be immediately overridden by\n * `syncTextTrackModes` on the next SPF state update.\n *\n * When a subtitle/caption track's mode is 'showing', its DOM `id` — which\n * matches the SPF track ID set by `setupTextTracks` — is written to\n * `selectedTextTrackId`. When no subtitle/caption track is 'showing',\n * `selectedTextTrackId` is cleared along with the deselected track's\n * `textBufferState` entry — setting mode to 'disabled' clears native cues from\n * the track element, so the buffer must be reset to re-fetch cues on re-enable.\n *\n * @example\n * const cleanup = syncSelectedTextTrackFromDom({ state, owners });\n */\nexport function syncSelectedTextTrackFromDom<\n S extends SelectedTextTrackFromDomState,\n O extends SelectedTextTrackFromDomOwners,\n>({ state, owners }: { state: Signal<S>; owners: Signal<O> }): () => void {\n const mediaElement = computed(() => owners.get().mediaElement);\n\n return effect(() => {\n const el = mediaElement.get();\n if (!el) return;\n\n return listen(el.textTracks, 'change', () => {\n const showingTrack = Array.from(el.textTracks).find(\n (t) => t.mode === 'showing' && (t.kind === 'subtitles' || t.kind === 'captions')\n );\n\n // showingTrack.id is set from the SPF presentation track ID by setupTextTracks.\n // Fall back to undefined for empty-string IDs (non-SPF-managed tracks).\n const newId = showingTrack?.id || undefined;\n const current = state.get();\n\n // Guard against redundant writes — e.g. syncTextTrackModes confirming the\n // current selection, which would otherwise create a feedback loop.\n if (current.selectedTextTrackId === newId) return;\n\n if (newId) {\n const patch: Partial<SelectedTextTrackFromDomState> = { selectedTextTrackId: newId };\n update(state, patch);\n } else {\n // When deselecting, clear the textBufferState entry for the previous track.\n // Setting mode to 'disabled' (as toggleSubtitles() does) clears native cues\n // from the track element, so the buffer must be reset to allow re-fetching\n // on re-enable.\n const prevId = current.selectedTextTrackId;\n if (prevId && current.textBufferState?.[prevId]) {\n const next = { ...current.textBufferState };\n delete next[prevId];\n const patch: Partial<SelectedTextTrackFromDomState> = {\n selectedTextTrackId: undefined,\n textBufferState: next,\n };\n update(state, patch);\n } else {\n const patch: Partial<SelectedTextTrackFromDomState> = { selectedTextTrackId: undefined };\n update(state, patch);\n }\n }\n });\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AA0CA,SAAgB,6BAGd,EAAE,OAAO,UAA+D;CACxE,MAAM,eAAe,eAAe,OAAO,KAAK,CAAC,aAAa;AAE9D,QAAO,aAAa;EAClB,MAAM,KAAK,aAAa,KAAK;AAC7B,MAAI,CAAC,GAAI;AAET,SAAO,OAAO,GAAG,YAAY,gBAAgB;GAO3C,MAAM,QANe,MAAM,KAAK,GAAG,WAAW,CAAC,MAC5C,MAAM,EAAE,SAAS,cAAc,EAAE,SAAS,eAAe,EAAE,SAAS,YACtE,EAI2B,MAAM,KAAA;GAClC,MAAM,UAAU,MAAM,KAAK;AAI3B,OAAI,QAAQ,wBAAwB,MAAO;AAE3C,OAAI,MAEF,QAAO,OAD+C,EAAE,qBAAqB,OAAO,CAChE;QACf;IAKL,MAAM,SAAS,QAAQ;AACvB,QAAI,UAAU,QAAQ,kBAAkB,SAAS;KAC/C,MAAM,OAAO,EAAE,GAAG,QAAQ,iBAAiB;AAC3C,YAAO,KAAK;AAKZ,YAAO,OAJ+C;MACpD,qBAAqB,KAAA;MACrB,iBAAiB;MAClB,CACmB;UAGpB,QAAO,OAD+C,EAAE,qBAAqB,KAAA,GAAW,CACpE;;IAGxB;GACF"}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { effect } from "../../core/signals/effect.js";
|
|
2
|
-
import { computed } from "../../core/signals/primitives.js";
|
|
3
|
-
//#region src/dom/features/sync-text-track-modes.ts
|
|
4
|
-
/**
|
|
5
|
-
* Sync text track modes orchestration.
|
|
6
|
-
*
|
|
7
|
-
* Manages track element modes based on selectedTextTrackId:
|
|
8
|
-
* - Selected track: mode = "showing"
|
|
9
|
-
* - Other tracks: mode = "hidden"
|
|
10
|
-
* - No selection: all tracks mode = "hidden"
|
|
11
|
-
*
|
|
12
|
-
* Note: Uses "hidden" instead of "disabled" for non-selected tracks
|
|
13
|
-
* so they remain available in the browser's track menu.
|
|
14
|
-
*
|
|
15
|
-
* @example
|
|
16
|
-
* const cleanup = syncTextTrackModes({ state, owners });
|
|
17
|
-
*/
|
|
18
|
-
function syncTextTrackModes({ state, owners }) {
|
|
19
|
-
const textTracksSignal = computed(() => owners.get().textTracks);
|
|
20
|
-
const selectedTextTrackIdSignal = computed(() => state.get().selectedTextTrackId);
|
|
21
|
-
const canSyncTextTrackModes = computed(() => !!textTracksSignal.get()?.size);
|
|
22
|
-
return effect(() => {
|
|
23
|
-
if (!canSyncTextTrackModes.get()) return;
|
|
24
|
-
/** @TODO refactor TextTracks owners model. Should simply use id. Also should use corresponding TextTrack (JS) element if possible (CJP) */
|
|
25
|
-
const textTracks = textTracksSignal.get();
|
|
26
|
-
const selectedTextTrackId = selectedTextTrackIdSignal.get();
|
|
27
|
-
for (const [trackId, trackElement] of textTracks) trackElement.track.mode = trackId === selectedTextTrackId ? "showing" : "hidden";
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
//#endregion
|
|
31
|
-
export { syncTextTrackModes };
|
|
32
|
-
|
|
33
|
-
//# sourceMappingURL=sync-text-track-modes.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sync-text-track-modes.js","names":[],"sources":["../../../../src/dom/features/sync-text-track-modes.ts"],"sourcesContent":["import { effect } from '../../core/signals/effect';\nimport { computed, type Signal } from '../../core/signals/primitives';\n\n/**\n * State shape for text track mode synchronization.\n */\nexport interface TextTrackModeState {\n selectedTextTrackId?: string | undefined;\n}\n\n/**\n * Owners shape for text track mode synchronization.\n */\nexport interface TextTrackModeOwners {\n textTracks?: Map<string, HTMLTrackElement>;\n}\n\n/**\n * Sync text track modes orchestration.\n *\n * Manages track element modes based on selectedTextTrackId:\n * - Selected track: mode = \"showing\"\n * - Other tracks: mode = \"hidden\"\n * - No selection: all tracks mode = \"hidden\"\n *\n * Note: Uses \"hidden\" instead of \"disabled\" for non-selected tracks\n * so they remain available in the browser's track menu.\n *\n * @example\n * const cleanup = syncTextTrackModes({ state, owners });\n */\nexport function syncTextTrackModes<S extends TextTrackModeState, O extends TextTrackModeOwners>({\n state,\n owners,\n}: {\n state: Signal<S>;\n owners: Signal<O>;\n}): () => void {\n const textTracksSignal = computed(() => owners.get().textTracks);\n const selectedTextTrackIdSignal = computed(() => state.get().selectedTextTrackId);\n\n const canSyncTextTrackModes = computed(() => !!textTracksSignal.get()?.size);\n\n return effect(() => {\n if (!canSyncTextTrackModes.get()) return;\n /** @TODO refactor TextTracks owners model. Should simply use id. Also should use corresponding TextTrack (JS) element if possible (CJP) */\n const textTracks = textTracksSignal.get() as Map<string, HTMLTrackElement>;\n const selectedTextTrackId = selectedTextTrackIdSignal.get() as string;\n\n for (const [trackId, trackElement] of textTracks) {\n trackElement.track.mode = trackId === selectedTextTrackId ? 'showing' : 'hidden';\n }\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AA+BA,SAAgB,mBAAgF,EAC9F,OACA,UAIa;CACb,MAAM,mBAAmB,eAAe,OAAO,KAAK,CAAC,WAAW;CAChE,MAAM,4BAA4B,eAAe,MAAM,KAAK,CAAC,oBAAoB;CAEjF,MAAM,wBAAwB,eAAe,CAAC,CAAC,iBAAiB,KAAK,EAAE,KAAK;AAE5E,QAAO,aAAa;AAClB,MAAI,CAAC,sBAAsB,KAAK,CAAE;;EAElC,MAAM,aAAa,iBAAiB,KAAK;EACzC,MAAM,sBAAsB,0BAA0B,KAAK;AAE3D,OAAK,MAAM,CAAC,SAAS,iBAAiB,WACpC,cAAa,MAAM,OAAO,YAAY,sBAAsB,YAAY;GAE1E"}
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { effect } from "../../core/signals/effect.js";
|
|
2
|
-
import { computed, update } from "../../core/signals/primitives.js";
|
|
3
|
-
//#region src/dom/features/setup-text-tracks.ts
|
|
4
|
-
/**
|
|
5
|
-
* Create a track element for a text track.
|
|
6
|
-
*
|
|
7
|
-
* Note: We use DOM <track> elements instead of the TextTrack JS API
|
|
8
|
-
* because there's no way to remove TextTracks added via addTextTrack().
|
|
9
|
-
*/
|
|
10
|
-
function createTrackElement(track) {
|
|
11
|
-
const trackElement = document.createElement("track");
|
|
12
|
-
trackElement.id = track.id;
|
|
13
|
-
trackElement.kind = track.kind;
|
|
14
|
-
trackElement.label = track.label;
|
|
15
|
-
if (track.language) trackElement.srclang = track.language;
|
|
16
|
-
if (track.default) trackElement.default = true;
|
|
17
|
-
trackElement.src = track.url;
|
|
18
|
-
return trackElement;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Setup text tracks orchestration.
|
|
22
|
-
*
|
|
23
|
-
* Triggers when:
|
|
24
|
-
* - mediaElement exists
|
|
25
|
-
* - presentation is resolved (has text tracks)
|
|
26
|
-
*
|
|
27
|
-
* Creates <track> elements for all text tracks and adds them as children
|
|
28
|
-
* to the media element. This allows the browser's native text track rendering.
|
|
29
|
-
*
|
|
30
|
-
* Note: Uses DOM track elements instead of TextTrack API because tracks
|
|
31
|
-
* added via addTextTrack() cannot be removed.
|
|
32
|
-
*
|
|
33
|
-
* @example
|
|
34
|
-
* const cleanup = setupTextTracks({ state, owners });
|
|
35
|
-
*/
|
|
36
|
-
function setupTextTracks({ state, owners }) {
|
|
37
|
-
const modelTextTracksSignal = computed(() => state.get().presentation?.selectionSets?.find((selectionSet) => selectionSet.type === "text")?.switchingSets[0]?.tracks, { equals(prevTextTracks, nextTextTracks) {
|
|
38
|
-
if (prevTextTracks === nextTextTracks) return true;
|
|
39
|
-
if (typeof prevTextTracks !== typeof nextTextTracks) return false;
|
|
40
|
-
if (prevTextTracks?.length !== nextTextTracks?.length) return false;
|
|
41
|
-
return !!nextTextTracks && nextTextTracks.every((nextTextTrack) => prevTextTracks?.some((prevTextTrack) => prevTextTrack.id === nextTextTrack.id));
|
|
42
|
-
} });
|
|
43
|
-
const ownerTextTracksSignal = computed(() => owners.get().textTracks);
|
|
44
|
-
const mediaElementSignal = computed(() => owners.get().mediaElement);
|
|
45
|
-
const canSetupTextTracksSignal = computed(() => !!mediaElementSignal.get() && modelTextTracksSignal.get()?.length);
|
|
46
|
-
const shouldSetupTextTracksSignal = computed(() => !ownerTextTracksSignal.get());
|
|
47
|
-
const cleanupEffect = effect(() => {
|
|
48
|
-
if (!canSetupTextTracksSignal.get() || !shouldSetupTextTracksSignal.get()) return;
|
|
49
|
-
const mediaElement = mediaElementSignal.get();
|
|
50
|
-
const modelTextTracks = modelTextTracksSignal.get();
|
|
51
|
-
const trackMap = /* @__PURE__ */ new Map();
|
|
52
|
-
modelTextTracks.forEach((modelTextTrack) => {
|
|
53
|
-
const trackElement = createTrackElement(modelTextTrack);
|
|
54
|
-
mediaElement.appendChild(trackElement);
|
|
55
|
-
trackMap.set(modelTextTrack.id, trackElement);
|
|
56
|
-
});
|
|
57
|
-
if (trackMap.size) update(owners, { textTracks: trackMap });
|
|
58
|
-
});
|
|
59
|
-
return () => {
|
|
60
|
-
owners.get().textTracks?.forEach((trackElement) => trackElement.remove());
|
|
61
|
-
cleanupEffect();
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
//#endregion
|
|
65
|
-
export { setupTextTracks };
|
|
66
|
-
|
|
67
|
-
//# sourceMappingURL=setup-text-tracks.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"setup-text-tracks.js","names":[],"sources":["../../../../src/dom/features/setup-text-tracks.ts"],"sourcesContent":["import { effect } from '../../core/signals/effect';\nimport { computed, type Signal, update } from '../../core/signals/primitives';\nimport type { PartiallyResolvedTextTrack, Presentation, TextTrack } from '../../core/types';\n\n/**\n * State shape for text track setup.\n */\nexport interface TextTrackState {\n presentation?: Presentation | undefined;\n selectedTextTrackId?: string | undefined;\n}\n\n/**\n * Owners shape for text track setup.\n */\nexport interface TextTrackOwners {\n mediaElement?: HTMLMediaElement | undefined;\n textTracks?: Map<string, HTMLTrackElement>;\n}\n\n/**\n * Create a track element for a text track.\n *\n * Note: We use DOM <track> elements instead of the TextTrack JS API\n * because there's no way to remove TextTracks added via addTextTrack().\n */\nfunction createTrackElement(track: PartiallyResolvedTextTrack | TextTrack): HTMLTrackElement {\n const trackElement = document.createElement('track');\n\n trackElement.id = track.id;\n trackElement.kind = track.kind;\n trackElement.label = track.label;\n\n if (track.language) {\n trackElement.srclang = track.language;\n }\n\n if (track.default) {\n trackElement.default = true;\n }\n\n // Set src to track URL\n trackElement.src = track.url;\n\n return trackElement;\n}\n\n/**\n * Setup text tracks orchestration.\n *\n * Triggers when:\n * - mediaElement exists\n * - presentation is resolved (has text tracks)\n *\n * Creates <track> elements for all text tracks and adds them as children\n * to the media element. This allows the browser's native text track rendering.\n *\n * Note: Uses DOM track elements instead of TextTrack API because tracks\n * added via addTextTrack() cannot be removed.\n *\n * @example\n * const cleanup = setupTextTracks({ state, owners });\n */\nexport function setupTextTracks<S extends TextTrackState, O extends TextTrackOwners>({\n state,\n owners,\n}: {\n state: Signal<S>;\n owners: Signal<O>;\n}): () => void {\n const modelTextTracksSignal = computed(\n // NOTE: This assumes exactly one selection set and switching set for TextTracks (CJP)\n () =>\n state.get().presentation?.selectionSets?.find((selectionSet) => selectionSet.type === 'text')?.switchingSets[0]\n ?.tracks,\n {\n /** @TODO Make generic and abstract away for Array<T> | undefined (CJP) */\n equals(prevTextTracks, nextTextTracks) {\n if (prevTextTracks === nextTextTracks) return true;\n if (typeof prevTextTracks !== typeof nextTextTracks) return false;\n if (prevTextTracks?.length !== nextTextTracks?.length) return false;\n // NOTE: This could probably be optimized, but the set should generally be small (CJP)\n return (\n !!nextTextTracks &&\n nextTextTracks.every((nextTextTrack) =>\n prevTextTracks?.some((prevTextTrack) => prevTextTrack.id === nextTextTrack.id)\n )\n );\n },\n }\n );\n const ownerTextTracksSignal = computed(() => owners.get().textTracks);\n const mediaElementSignal = computed(() => owners.get().mediaElement);\n\n const canSetupTextTracksSignal = computed(() => !!mediaElementSignal.get() && modelTextTracksSignal.get()?.length);\n const shouldSetupTextTracksSignal = computed(() => !ownerTextTracksSignal.get());\n\n const cleanupEffect = effect(() => {\n if (!canSetupTextTracksSignal.get() || !shouldSetupTextTracksSignal.get()) return;\n const mediaElement = mediaElementSignal.get();\n const modelTextTracks = modelTextTracksSignal.get() as TextTrack[];\n\n const trackMap = new Map<string, HTMLTrackElement>();\n modelTextTracks.forEach((modelTextTrack) => {\n const trackElement = createTrackElement(modelTextTrack);\n mediaElement!.appendChild(trackElement);\n trackMap.set(modelTextTrack.id, trackElement);\n });\n\n if (trackMap.size) {\n const patch: Partial<TextTrackOwners> = { textTracks: trackMap };\n update(owners, patch);\n }\n });\n\n return () => {\n owners.get().textTracks?.forEach((trackElement) => trackElement.remove());\n cleanupEffect();\n };\n}\n"],"mappings":";;;;;;;;;AA0BA,SAAS,mBAAmB,OAAiE;CAC3F,MAAM,eAAe,SAAS,cAAc,QAAQ;AAEpD,cAAa,KAAK,MAAM;AACxB,cAAa,OAAO,MAAM;AAC1B,cAAa,QAAQ,MAAM;AAE3B,KAAI,MAAM,SACR,cAAa,UAAU,MAAM;AAG/B,KAAI,MAAM,QACR,cAAa,UAAU;AAIzB,cAAa,MAAM,MAAM;AAEzB,QAAO;;;;;;;;;;;;;;;;;;AAmBT,SAAgB,gBAAqE,EACnF,OACA,UAIa;CACb,MAAM,wBAAwB,eAG1B,MAAM,KAAK,CAAC,cAAc,eAAe,MAAM,iBAAiB,aAAa,SAAS,OAAO,EAAE,cAAc,IACzG,QACN,EAEE,OAAO,gBAAgB,gBAAgB;AACrC,MAAI,mBAAmB,eAAgB,QAAO;AAC9C,MAAI,OAAO,mBAAmB,OAAO,eAAgB,QAAO;AAC5D,MAAI,gBAAgB,WAAW,gBAAgB,OAAQ,QAAO;AAE9D,SACE,CAAC,CAAC,kBACF,eAAe,OAAO,kBACpB,gBAAgB,MAAM,kBAAkB,cAAc,OAAO,cAAc,GAAG,CAC/E;IAGN,CACF;CACD,MAAM,wBAAwB,eAAe,OAAO,KAAK,CAAC,WAAW;CACrE,MAAM,qBAAqB,eAAe,OAAO,KAAK,CAAC,aAAa;CAEpE,MAAM,2BAA2B,eAAe,CAAC,CAAC,mBAAmB,KAAK,IAAI,sBAAsB,KAAK,EAAE,OAAO;CAClH,MAAM,8BAA8B,eAAe,CAAC,sBAAsB,KAAK,CAAC;CAEhF,MAAM,gBAAgB,aAAa;AACjC,MAAI,CAAC,yBAAyB,KAAK,IAAI,CAAC,4BAA4B,KAAK,CAAE;EAC3E,MAAM,eAAe,mBAAmB,KAAK;EAC7C,MAAM,kBAAkB,sBAAsB,KAAK;EAEnD,MAAM,2BAAW,IAAI,KAA+B;AACpD,kBAAgB,SAAS,mBAAmB;GAC1C,MAAM,eAAe,mBAAmB,eAAe;AACvD,gBAAc,YAAY,aAAa;AACvC,YAAS,IAAI,eAAe,IAAI,aAAa;IAC7C;AAEF,MAAI,SAAS,KAEX,QAAO,QADiC,EAAE,YAAY,UAAU,CAC3C;GAEvB;AAEF,cAAa;AACX,SAAO,KAAK,CAAC,YAAY,SAAS,iBAAiB,aAAa,QAAQ,CAAC;AACzE,iBAAe"}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { effect } from "../../core/signals/effect.js";
|
|
2
|
-
import { computed, update } from "../../core/signals/primitives.js";
|
|
3
|
-
import { listen } from "@videojs/utils/dom";
|
|
4
|
-
//#region src/dom/features/sync-selected-text-track-from-dom.ts
|
|
5
|
-
/**
|
|
6
|
-
* Sync selectedTextTrackId from DOM text track mode changes.
|
|
7
|
-
*
|
|
8
|
-
* Listens to the `change` event on `media.textTracks` and updates
|
|
9
|
-
* `selectedTextTrackId` when external code (e.g. the captions button via
|
|
10
|
-
* `toggleSubtitles()`) changes a subtitle/caption track mode to 'showing'.
|
|
11
|
-
*
|
|
12
|
-
* This bridges the core store's `toggleSubtitles()` with SPF's reactive text
|
|
13
|
-
* track pipeline (`syncTextTrackModes`, `loadTextTrackCues`). Without this
|
|
14
|
-
* bridge, direct DOM mode changes would be immediately overridden by
|
|
15
|
-
* `syncTextTrackModes` on the next SPF state update.
|
|
16
|
-
*
|
|
17
|
-
* When a subtitle/caption track's mode is 'showing', its DOM `id` — which
|
|
18
|
-
* matches the SPF track ID set by `setupTextTracks` — is written to
|
|
19
|
-
* `selectedTextTrackId`. When no subtitle/caption track is 'showing',
|
|
20
|
-
* `selectedTextTrackId` is cleared along with the deselected track's
|
|
21
|
-
* `textBufferState` entry — setting mode to 'disabled' clears native cues from
|
|
22
|
-
* the track element, so the buffer must be reset to re-fetch cues on re-enable.
|
|
23
|
-
*
|
|
24
|
-
* @example
|
|
25
|
-
* const cleanup = syncSelectedTextTrackFromDom({ state, owners });
|
|
26
|
-
*/
|
|
27
|
-
function syncSelectedTextTrackFromDom({ state, owners }) {
|
|
28
|
-
const mediaElement = computed(() => owners.get().mediaElement);
|
|
29
|
-
return effect(() => {
|
|
30
|
-
const el = mediaElement.get();
|
|
31
|
-
if (!el) return;
|
|
32
|
-
return listen(el.textTracks, "change", () => {
|
|
33
|
-
const newId = Array.from(el.textTracks).find((t) => t.mode === "showing" && (t.kind === "subtitles" || t.kind === "captions"))?.id || void 0;
|
|
34
|
-
const current = state.get();
|
|
35
|
-
if (current.selectedTextTrackId === newId) return;
|
|
36
|
-
if (newId) update(state, { selectedTextTrackId: newId });
|
|
37
|
-
else {
|
|
38
|
-
const prevId = current.selectedTextTrackId;
|
|
39
|
-
if (prevId && current.textBufferState?.[prevId]) {
|
|
40
|
-
const next = { ...current.textBufferState };
|
|
41
|
-
delete next[prevId];
|
|
42
|
-
update(state, {
|
|
43
|
-
selectedTextTrackId: void 0,
|
|
44
|
-
textBufferState: next
|
|
45
|
-
});
|
|
46
|
-
} else update(state, { selectedTextTrackId: void 0 });
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
//#endregion
|
|
52
|
-
export { syncSelectedTextTrackFromDom };
|
|
53
|
-
|
|
54
|
-
//# sourceMappingURL=sync-selected-text-track-from-dom.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sync-selected-text-track-from-dom.js","names":[],"sources":["../../../../src/dom/features/sync-selected-text-track-from-dom.ts"],"sourcesContent":["import { listen } from '@videojs/utils/dom';\nimport { effect } from '../../core/signals/effect';\nimport { computed, type Signal, update } from '../../core/signals/primitives';\nimport type { TextTrackBufferState } from './load-text-track-cues';\n\n/**\n * State shape for DOM-driven text track selection.\n */\nexport interface SelectedTextTrackFromDomState {\n selectedTextTrackId?: string | undefined;\n textBufferState?: TextTrackBufferState | undefined;\n}\n\n/**\n * Owners shape for DOM-driven text track selection.\n */\nexport interface SelectedTextTrackFromDomOwners {\n mediaElement?: HTMLMediaElement | undefined;\n}\n\n/**\n * Sync selectedTextTrackId from DOM text track mode changes.\n *\n * Listens to the `change` event on `media.textTracks` and updates\n * `selectedTextTrackId` when external code (e.g. the captions button via\n * `toggleSubtitles()`) changes a subtitle/caption track mode to 'showing'.\n *\n * This bridges the core store's `toggleSubtitles()` with SPF's reactive text\n * track pipeline (`syncTextTrackModes`, `loadTextTrackCues`). Without this\n * bridge, direct DOM mode changes would be immediately overridden by\n * `syncTextTrackModes` on the next SPF state update.\n *\n * When a subtitle/caption track's mode is 'showing', its DOM `id` — which\n * matches the SPF track ID set by `setupTextTracks` — is written to\n * `selectedTextTrackId`. When no subtitle/caption track is 'showing',\n * `selectedTextTrackId` is cleared along with the deselected track's\n * `textBufferState` entry — setting mode to 'disabled' clears native cues from\n * the track element, so the buffer must be reset to re-fetch cues on re-enable.\n *\n * @example\n * const cleanup = syncSelectedTextTrackFromDom({ state, owners });\n */\nexport function syncSelectedTextTrackFromDom<\n S extends SelectedTextTrackFromDomState,\n O extends SelectedTextTrackFromDomOwners,\n>({ state, owners }: { state: Signal<S>; owners: Signal<O> }): () => void {\n const mediaElement = computed(() => owners.get().mediaElement);\n\n return effect(() => {\n const el = mediaElement.get();\n if (!el) return;\n\n return listen(el.textTracks, 'change', () => {\n const showingTrack = Array.from(el.textTracks).find(\n (t) => t.mode === 'showing' && (t.kind === 'subtitles' || t.kind === 'captions')\n );\n\n // showingTrack.id is set from the SPF presentation track ID by setupTextTracks.\n // Fall back to undefined for empty-string IDs (non-SPF-managed tracks).\n const newId = showingTrack?.id || undefined;\n const current = state.get();\n\n // Guard against redundant writes — e.g. syncTextTrackModes confirming the\n // current selection, which would otherwise create a feedback loop.\n if (current.selectedTextTrackId === newId) return;\n\n if (newId) {\n const patch: Partial<SelectedTextTrackFromDomState> = { selectedTextTrackId: newId };\n update(state, patch);\n } else {\n // When deselecting, clear the textBufferState entry for the previous track.\n // Setting mode to 'disabled' (as toggleSubtitles() does) clears native cues\n // from the track element, so the buffer must be reset to allow re-fetching\n // on re-enable.\n const prevId = current.selectedTextTrackId;\n if (prevId && current.textBufferState?.[prevId]) {\n const next = { ...current.textBufferState };\n delete next[prevId];\n const patch: Partial<SelectedTextTrackFromDomState> = {\n selectedTextTrackId: undefined,\n textBufferState: next,\n };\n update(state, patch);\n } else {\n const patch: Partial<SelectedTextTrackFromDomState> = { selectedTextTrackId: undefined };\n update(state, patch);\n }\n }\n });\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AA0CA,SAAgB,6BAGd,EAAE,OAAO,UAA+D;CACxE,MAAM,eAAe,eAAe,OAAO,KAAK,CAAC,aAAa;AAE9D,QAAO,aAAa;EAClB,MAAM,KAAK,aAAa,KAAK;AAC7B,MAAI,CAAC,GAAI;AAET,SAAO,OAAO,GAAG,YAAY,gBAAgB;GAO3C,MAAM,QANe,MAAM,KAAK,GAAG,WAAW,CAAC,MAC5C,MAAM,EAAE,SAAS,cAAc,EAAE,SAAS,eAAe,EAAE,SAAS,YACtE,EAI2B,MAAM,KAAA;GAClC,MAAM,UAAU,MAAM,KAAK;AAI3B,OAAI,QAAQ,wBAAwB,MAAO;AAE3C,OAAI,MAEF,QAAO,OAD+C,EAAE,qBAAqB,OAAO,CAChE;QACf;IAKL,MAAM,SAAS,QAAQ;AACvB,QAAI,UAAU,QAAQ,kBAAkB,SAAS;KAC/C,MAAM,OAAO,EAAE,GAAG,QAAQ,iBAAiB;AAC3C,YAAO,KAAK;AAKZ,YAAO,OAJ+C;MACpD,qBAAqB,KAAA;MACrB,iBAAiB;MAClB,CACmB;UAGpB,QAAO,OAD+C,EAAE,qBAAqB,KAAA,GAAW,CACpE;;IAGxB;GACF"}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { effect } from "../../core/signals/effect.js";
|
|
2
|
-
import { computed } from "../../core/signals/primitives.js";
|
|
3
|
-
//#region src/dom/features/sync-text-track-modes.ts
|
|
4
|
-
/**
|
|
5
|
-
* Sync text track modes orchestration.
|
|
6
|
-
*
|
|
7
|
-
* Manages track element modes based on selectedTextTrackId:
|
|
8
|
-
* - Selected track: mode = "showing"
|
|
9
|
-
* - Other tracks: mode = "hidden"
|
|
10
|
-
* - No selection: all tracks mode = "hidden"
|
|
11
|
-
*
|
|
12
|
-
* Note: Uses "hidden" instead of "disabled" for non-selected tracks
|
|
13
|
-
* so they remain available in the browser's track menu.
|
|
14
|
-
*
|
|
15
|
-
* @example
|
|
16
|
-
* const cleanup = syncTextTrackModes({ state, owners });
|
|
17
|
-
*/
|
|
18
|
-
function syncTextTrackModes({ state, owners }) {
|
|
19
|
-
const textTracksSignal = computed(() => owners.get().textTracks);
|
|
20
|
-
const selectedTextTrackIdSignal = computed(() => state.get().selectedTextTrackId);
|
|
21
|
-
const canSyncTextTrackModes = computed(() => !!textTracksSignal.get()?.size);
|
|
22
|
-
return effect(() => {
|
|
23
|
-
if (!canSyncTextTrackModes.get()) return;
|
|
24
|
-
/** @TODO refactor TextTracks owners model. Should simply use id. Also should use corresponding TextTrack (JS) element if possible (CJP) */
|
|
25
|
-
const textTracks = textTracksSignal.get();
|
|
26
|
-
const selectedTextTrackId = selectedTextTrackIdSignal.get();
|
|
27
|
-
for (const [trackId, trackElement] of textTracks) trackElement.track.mode = trackId === selectedTextTrackId ? "showing" : "hidden";
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
//#endregion
|
|
31
|
-
export { syncTextTrackModes };
|
|
32
|
-
|
|
33
|
-
//# sourceMappingURL=sync-text-track-modes.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sync-text-track-modes.js","names":[],"sources":["../../../../src/dom/features/sync-text-track-modes.ts"],"sourcesContent":["import { effect } from '../../core/signals/effect';\nimport { computed, type Signal } from '../../core/signals/primitives';\n\n/**\n * State shape for text track mode synchronization.\n */\nexport interface TextTrackModeState {\n selectedTextTrackId?: string | undefined;\n}\n\n/**\n * Owners shape for text track mode synchronization.\n */\nexport interface TextTrackModeOwners {\n textTracks?: Map<string, HTMLTrackElement>;\n}\n\n/**\n * Sync text track modes orchestration.\n *\n * Manages track element modes based on selectedTextTrackId:\n * - Selected track: mode = \"showing\"\n * - Other tracks: mode = \"hidden\"\n * - No selection: all tracks mode = \"hidden\"\n *\n * Note: Uses \"hidden\" instead of \"disabled\" for non-selected tracks\n * so they remain available in the browser's track menu.\n *\n * @example\n * const cleanup = syncTextTrackModes({ state, owners });\n */\nexport function syncTextTrackModes<S extends TextTrackModeState, O extends TextTrackModeOwners>({\n state,\n owners,\n}: {\n state: Signal<S>;\n owners: Signal<O>;\n}): () => void {\n const textTracksSignal = computed(() => owners.get().textTracks);\n const selectedTextTrackIdSignal = computed(() => state.get().selectedTextTrackId);\n\n const canSyncTextTrackModes = computed(() => !!textTracksSignal.get()?.size);\n\n return effect(() => {\n if (!canSyncTextTrackModes.get()) return;\n /** @TODO refactor TextTracks owners model. Should simply use id. Also should use corresponding TextTrack (JS) element if possible (CJP) */\n const textTracks = textTracksSignal.get() as Map<string, HTMLTrackElement>;\n const selectedTextTrackId = selectedTextTrackIdSignal.get() as string;\n\n for (const [trackId, trackElement] of textTracks) {\n trackElement.track.mode = trackId === selectedTextTrackId ? 'showing' : 'hidden';\n }\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AA+BA,SAAgB,mBAAgF,EAC9F,OACA,UAIa;CACb,MAAM,mBAAmB,eAAe,OAAO,KAAK,CAAC,WAAW;CAChE,MAAM,4BAA4B,eAAe,MAAM,KAAK,CAAC,oBAAoB;CAEjF,MAAM,wBAAwB,eAAe,CAAC,CAAC,iBAAiB,KAAK,EAAE,KAAK;AAE5E,QAAO,aAAa;AAClB,MAAI,CAAC,sBAAsB,KAAK,CAAE;;EAElC,MAAM,aAAa,iBAAiB,KAAK;EACzC,MAAM,sBAAsB,0BAA0B,KAAK;AAE3D,OAAK,MAAM,CAAC,SAAS,iBAAiB,WACpC,cAAa,MAAM,OAAO,YAAY,sBAAsB,YAAY;GAE1E"}
|