@yaneart/yaneplayer 0.0.0-stage → 0.1.0

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.
Files changed (81) hide show
  1. package/GUIDE.md +710 -0
  2. package/LICENSE +21 -0
  3. package/README.md +62 -2
  4. package/THIRD_PARTY_NOTICES.md +46 -0
  5. package/assets/player-preview.jpg +0 -0
  6. package/dist/ass-renderer-BnMSusia.js +84 -0
  7. package/dist/auto-captions/assets.js +9 -0
  8. package/dist/auto-captions/audio.js +42 -0
  9. package/dist/auto-captions/ort-wasm-simd-threaded.asyncify.mjs +124 -0
  10. package/dist/auto-captions/ort-wasm-simd-threaded.asyncify.wasm +0 -0
  11. package/dist/auto-captions/translation-worker.js +49 -0
  12. package/dist/auto-captions/worker.js +51 -0
  13. package/dist/core/ass-renderer.d.ts +5 -0
  14. package/dist/core/async.d.ts +5 -0
  15. package/dist/core/auto-caption-assets.d.ts +7 -0
  16. package/dist/core/auto-captions.d.ts +14 -0
  17. package/dist/core/caption-publication.d.ts +22 -0
  18. package/dist/core/commands.d.ts +10 -0
  19. package/dist/core/errors.d.ts +7 -0
  20. package/dist/core/hls.d.ts +4 -0
  21. package/dist/core/index.d.ts +6 -0
  22. package/dist/core/platform.d.ts +22 -0
  23. package/dist/core/preferences.d.ts +10 -0
  24. package/dist/core/runtime.d.ts +3 -0
  25. package/dist/core/source.d.ts +6 -0
  26. package/dist/core/subtitle-assets.d.ts +6 -0
  27. package/dist/core/subtitle-data.d.ts +11 -0
  28. package/dist/core/subtitles.d.ts +22 -0
  29. package/dist/core/tracks.d.ts +7 -0
  30. package/dist/core/translation-model.d.ts +6 -0
  31. package/dist/core/transport.d.ts +15 -0
  32. package/dist/core/types.d.ts +331 -0
  33. package/dist/core-DSvEY8F-.js +3347 -0
  34. package/dist/core.js +1 -0
  35. package/dist/element.d.ts +89 -0
  36. package/dist/hls-Cll783tL.js +20700 -0
  37. package/dist/index.d.ts +4 -0
  38. package/dist/index.js +4771 -0
  39. package/dist/licenses/@fontsource-variable-manrope.txt +93 -0
  40. package/dist/licenses/@huggingface-jinja.txt +21 -0
  41. package/dist/licenses/@huggingface-tokenizers.txt +201 -0
  42. package/dist/licenses/@huggingface-transformers.txt +202 -0
  43. package/dist/licenses/@mingcute-icons.txt +202 -0
  44. package/dist/licenses/@videojs-core.txt +20 -0
  45. package/dist/licenses/@videojs-element.txt +20 -0
  46. package/dist/licenses/@videojs-hlsjs-video.txt +20 -0
  47. package/dist/licenses/@videojs-html.txt +20 -0
  48. package/dist/licenses/@videojs-media.txt +20 -0
  49. package/dist/licenses/@videojs-native-hls-video.txt +20 -0
  50. package/dist/licenses/@videojs-spf.txt +20 -0
  51. package/dist/licenses/@videojs-store.txt +20 -0
  52. package/dist/licenses/@videojs-utils.txt +20 -0
  53. package/dist/licenses/flatbuffers.txt +202 -0
  54. package/dist/licenses/hls.js.txt +28 -0
  55. package/dist/licenses/jassub.txt +22 -0
  56. package/dist/licenses/long.txt +202 -0
  57. package/dist/licenses/platform.txt +21 -0
  58. package/dist/licenses/protobufjs.txt +39 -0
  59. package/dist/licenses/third-party.txt +7941 -0
  60. package/dist/react.d.ts +56 -0
  61. package/dist/subtitles/assets.js +7 -0
  62. package/dist/subtitles/default.woff2 +0 -0
  63. package/dist/subtitles/jassub.js +1 -0
  64. package/dist/subtitles/wasm/jassub-worker-modern.wasm +0 -0
  65. package/dist/subtitles/wasm/jassub-worker.wasm +0 -0
  66. package/dist/subtitles/worker/worker.js +144 -0
  67. package/dist/ui/auto-captions.d.ts +6 -0
  68. package/dist/ui/captions.d.ts +6 -0
  69. package/dist/ui/controls.d.ts +28 -0
  70. package/dist/ui/fonts.d.ts +2 -0
  71. package/dist/ui/generated-captions.d.ts +7 -0
  72. package/dist/ui/icons.d.ts +24 -0
  73. package/dist/ui/labels.d.ts +74 -0
  74. package/dist/ui/rolling-captions.d.ts +10 -0
  75. package/dist/ui/settings.d.ts +17 -0
  76. package/dist/ui/speed.d.ts +9 -0
  77. package/dist/ui/subtitle-settings.d.ts +5 -0
  78. package/dist/ui/tooltips.d.ts +5 -0
  79. package/dist/ui/touch.d.ts +5 -0
  80. package/dist/ui/types.d.ts +1 -0
  81. package/package.json +77 -4
@@ -0,0 +1,5 @@
1
+ /** Keep renderer-specific lifecycle details outside playback/transport/UI. */
2
+ export declare function createAssRenderer(video: HTMLVideoElement, content: string, fonts: Uint8Array[], timeOffset: number, signal: AbortSignal, failed: () => void): Promise<{
3
+ dispose: () => void;
4
+ setOffset(value: number): Promise<void>;
5
+ }>;
@@ -0,0 +1,5 @@
1
+ export declare const aborted: () => DOMException;
2
+ export declare function checkSignal(signal: AbortSignal): void;
3
+ /** Register before invoking the action, including actions which dispatch synchronously. */
4
+ export declare function confirmation(subscribe: (check: () => void) => () => void, predicate: () => boolean, signal: AbortSignal, timeoutMs: number, action?: () => unknown): Promise<void>;
5
+ export declare function untilAborted<T>(promise: Promise<T>, signal: AbortSignal): Promise<T>;
@@ -0,0 +1,7 @@
1
+ export declare const autoCaptionAssets: {
2
+ worker: string;
3
+ translationWorker: string;
4
+ worklet: string;
5
+ wasm: string;
6
+ wasmModule: string;
7
+ };
@@ -0,0 +1,14 @@
1
+ import type { AutoCaptionState } from "./types.js";
2
+ /** Owns recognition resources only. The video remains the sole playback clock/output. */
3
+ export declare function createAutoCaptions(video: HTMLVideoElement, changed: () => void, captions: {
4
+ open(): void;
5
+ clear(): void;
6
+ show(text: string, segment: number): void;
7
+ off(): void;
8
+ }): {
9
+ readonly state: AutoCaptionState;
10
+ update(readySource: boolean, preparedTracks: boolean, pending: boolean, selectedAudio: string | null): void;
11
+ off: () => void;
12
+ setEnabled(value: boolean): Promise<void>;
13
+ dispose(): void;
14
+ };
@@ -0,0 +1,22 @@
1
+ export type CaptionFragment = {
2
+ segment: string;
3
+ offset: number;
4
+ text: string;
5
+ position: number;
6
+ };
7
+ /** Mutable recognition stays here. Only complete source fragments cross into MT/UI. */
8
+ export declare class CaptionPublication {
9
+ private cues;
10
+ private tail;
11
+ readonly stats: {
12
+ blocked: number;
13
+ evicted: number;
14
+ overlap: number;
15
+ };
16
+ update(segment: string, text: string, now: number, position: number): void;
17
+ next(now: number): CaptionFragment | undefined;
18
+ contains(job: CaptionFragment): boolean;
19
+ complete(job: CaptionFragment, published?: boolean): boolean;
20
+ private reconcile;
21
+ private compatible;
22
+ }
@@ -0,0 +1,10 @@
1
+ import type { CommandType, HostCommand } from "./types.js";
2
+ export type CommandInput = HostCommand extends infer C ? C extends HostCommand ? Omit<C, "target"> : never : never;
3
+ /** Keep the active execute awaited even after abort: abort is not server rollback. */
4
+ export declare function commandQueue(execute: (command: HostCommand, id: number, signal: AbortSignal) => Promise<void>, pending: (value: {
5
+ type: CommandType;
6
+ commandId: number;
7
+ } | null) => void, nextId: () => number): {
8
+ enqueue(command: CommandInput): Promise<void>;
9
+ clear(): void;
10
+ };
@@ -0,0 +1,7 @@
1
+ import type { PlayerError } from "./types.js";
2
+ export declare class RuntimeError extends Error {
3
+ readonly detail: PlayerError;
4
+ constructor(detail: PlayerError);
5
+ }
6
+ export declare function failure(code: PlayerError["code"], stage: PlayerError["stage"], message: string, retryable?: boolean): RuntimeError;
7
+ export declare function safeError(error: unknown, stage: PlayerError["stage"]): RuntimeError;
@@ -0,0 +1,4 @@
1
+ import type { PlayerSource } from "./types.js";
2
+ import type { Transport } from "./transport.js";
3
+ export declare const supportsMse: () => boolean;
4
+ export declare function hlsTransport(video: HTMLVideoElement, source: PlayerSource): Transport;
@@ -0,0 +1,6 @@
1
+ import type { PlayerOptions, PlayerRuntime } from "./types.js";
2
+ /** Package version; importing core does not create a player or access the DOM. */
3
+ export declare const version: string;
4
+ export type * from "./types.js";
5
+ /** Browser-only factory. Importing this module does not access the DOM or create media. */
6
+ export declare function createPlayer(options: PlayerOptions): PlayerRuntime;
@@ -0,0 +1,22 @@
1
+ import type { VideoPlayerStore } from "@videojs/core/dom";
2
+ import type { PlatformOptions, PlatformState, PlayerRuntime, PlayerSnapshot, PlayerSource } from "./types.js";
3
+ export declare function createPlatform(video: HTMLVideoElement, container: HTMLElement, store: VideoPlayerStore, changed: () => void, runtime: () => PlayerRuntime, initial?: PlatformOptions, timeoutMs?: number): {
4
+ bindMedia(media: object): () => void;
5
+ capabilities(): {
6
+ fullscreen: boolean;
7
+ pictureInPicture: boolean;
8
+ wakeLock: boolean;
9
+ mediaSession: boolean;
10
+ orientation: boolean;
11
+ preservesPitch: boolean;
12
+ };
13
+ readonly state: PlatformState;
14
+ update(next: PlayerSnapshot): void;
15
+ setSource(next: PlayerSource | null): void;
16
+ setOptions(next: PlatformOptions): void;
17
+ enterFullscreen: () => Promise<void>;
18
+ exitFullscreen: () => Promise<void>;
19
+ requestPictureInPicture: () => Promise<void>;
20
+ exitPictureInPicture: () => Promise<void>;
21
+ dispose(): void;
22
+ };
@@ -0,0 +1,10 @@
1
+ import type { PlayerPreferences, PlayerSnapshot, PreferencesAdapter } from "./types.js";
2
+ /** Validate the whole record before applying any part of an untrusted host response. */
3
+ export declare function validatePlayerPreferences(value: PlayerPreferences): PlayerPreferences;
4
+ export declare function createPreferences(apply: (value: PlayerPreferences, signal: AbortSignal) => Promise<void>, capture: () => PlayerPreferences, changed: () => void): {
5
+ readonly state: PlayerSnapshot["preferences"];
6
+ readonly applying: boolean;
7
+ setAdapter(next?: PreferencesAdapter): Promise<void>;
8
+ schedule(): void;
9
+ dispose(): void;
10
+ };
@@ -0,0 +1,3 @@
1
+ import type { PlayerOptions, PlayerRuntime } from "./types.js";
2
+ /** Single coordinator owning its foundation store and transport lifecycle. */
3
+ export declare function createRuntime(options: PlayerOptions): PlayerRuntime;
@@ -0,0 +1,6 @@
1
+ import type { PlayerSource, TimeRange } from "./types.js";
2
+ export declare function freeze<T>(value: T): T;
3
+ export declare function validateSource(source: PlayerSource, base: string): PlayerSource;
4
+ export declare function offset(source: PlayerSource | null): number | null;
5
+ export declare function absoluteRanges(ranges: readonly TimeRange[], anchor: number | null): TimeRange[];
6
+ export declare const contains: (ranges: readonly TimeRange[], time: number) => boolean;
@@ -0,0 +1,6 @@
1
+ export declare const subtitleAssets: {
2
+ worker: string;
3
+ wasm: string;
4
+ modernWasm: string;
5
+ font: string;
6
+ };
@@ -0,0 +1,11 @@
1
+ import type { SubtitleResource, SubtitleTrack } from "./types.js";
2
+ export declare const MiB: number;
3
+ export declare const subtitleId: (id: string) => string;
4
+ export declare const localSubtitleId = "subtitle:local";
5
+ export declare const subtitleFailure: (message: string) => import("./errors.js").RuntimeError;
6
+ export declare function validateSubtitles(tracks: readonly SubtitleTrack[] | undefined, base: string): void;
7
+ export declare function resourceURL(resource: SubtitleResource, base: string): URL;
8
+ /** The stream is bounded even when Content-Length is absent or dishonest. */
9
+ export declare function readResource(resource: SubtitleResource, base: string, limit: number, signal: AbortSignal): Promise<Uint8Array<ArrayBuffer>>;
10
+ export declare function decodeSubtitle(data: Uint8Array): string;
11
+ export declare function prepareSubtitle(text: string, format: string): string;
@@ -0,0 +1,22 @@
1
+ import type { CaptionState, CaptionStyle, PlayerSource, PlayerTrack } from "./types.js";
2
+ export declare const defaultCaptionStyle: CaptionStyle;
3
+ /** Only subtitle resources/settings live here; playback remains in the foundation store. */
4
+ export declare function createSubtitles(video: HTMLVideoElement, changed: () => void, timeoutMs: number, failed: (cause: unknown) => void): {
5
+ tracks: () => PlayerTrack[];
6
+ readonly selected: string | null;
7
+ readonly localActive: boolean;
8
+ readonly state: CaptionState;
9
+ owns: (id: string | null | undefined) => boolean;
10
+ select: (id: string, signal: AbortSignal) => Promise<void>;
11
+ openAuto(): void;
12
+ clearAuto(): void;
13
+ showAuto(text: string, segment: number): void;
14
+ off(): void;
15
+ transport(showing: boolean, hostRendered: boolean): void;
16
+ setSource(next: PlayerSource, keepLocal?: boolean): void;
17
+ loadFile(file: File, signal: AbortSignal, current: () => boolean): Promise<void>;
18
+ removeLocal(): void;
19
+ setDelay(value: number): Promise<void>;
20
+ setStyle(value: Partial<CaptionStyle>): void;
21
+ dispose(): void;
22
+ };
@@ -0,0 +1,7 @@
1
+ import type { PlayerTrack, TrackPreferences } from "./types.js";
2
+ /** Ordered language matching; accessibility flags are strict filters, default breaks ties. */
3
+ export declare function preferredTrack(tracks: readonly PlayerTrack[], languages?: readonly string[], flags?: {
4
+ forced?: boolean;
5
+ sdh?: boolean;
6
+ }): PlayerTrack | undefined;
7
+ export declare function validatePreferences(value: TrackPreferences | undefined): void;
@@ -0,0 +1,6 @@
1
+ export declare const translationModel: {
2
+ model: string;
3
+ revision: string;
4
+ bytes: number;
5
+ files: string[];
6
+ };
@@ -0,0 +1,15 @@
1
+ import type { HlsJsAdapter } from "@videojs/hlsjs-video";
2
+ import type { PlayerSource, PlayerTrack } from "./types.js";
3
+ export interface Transport {
4
+ readonly media: HTMLVideoElement | HlsJsAdapter;
5
+ /** HLS foundation marks audio enabled at SWITCHING; expose SWITCHED confirmation. */
6
+ readonly confirmedAudioId?: string | null;
7
+ disableText?(): void;
8
+ textReady?(id: string): boolean;
9
+ refreshText?(id: string): void;
10
+ trackMetadata?(kind: "audio" | "text", track: PlayerTrack): Partial<PlayerTrack>;
11
+ attach(): void;
12
+ start(): void | Promise<void>;
13
+ destroy(): void;
14
+ }
15
+ export declare function prepareTransport(video: HTMLVideoElement, source: PlayerSource): Promise<Transport>;
@@ -0,0 +1,331 @@
1
+ export type Playback = "idle" | "loading" | "ready" | "playing" | "paused" | "buffering" | "ended" | "error";
2
+ export type Intent = "playing" | "paused";
3
+ export type TimeRange = readonly [start: number, end: number];
4
+ export type Controller = "none" | "transport" | "host";
5
+ export type CommandType = "seek" | "audio" | "text" | "quality";
6
+ export interface PlayerTimeline {
7
+ readonly mode: "vod" | "live" | "windowed-vod";
8
+ readonly absoluteAnchorSeconds: number | null;
9
+ readonly mediaAnchorSeconds: number | null;
10
+ readonly durationSeconds: number | null;
11
+ /** Actual local media ranges in absolute coordinates, never torrent download ranges. */
12
+ readonly seekableRanges?: readonly TimeRange[];
13
+ }
14
+ export interface PlayerTrack {
15
+ readonly id: string;
16
+ readonly label?: string;
17
+ readonly language?: string;
18
+ readonly channels?: string;
19
+ readonly default?: boolean;
20
+ readonly forced?: boolean;
21
+ readonly sdh?: boolean;
22
+ readonly description?: string;
23
+ readonly codec?: string;
24
+ readonly format?: string;
25
+ readonly available?: boolean;
26
+ readonly unavailableReason?: string;
27
+ }
28
+ export interface RequestPolicy {
29
+ readonly allowedOrigins: readonly string[];
30
+ readonly credentials?: RequestCredentials;
31
+ /** Sent only to the original manifest origin. Never copied to another allowed origin. */
32
+ readonly headers?: Readonly<Record<string, string>>;
33
+ }
34
+ export interface SubtitleResource {
35
+ readonly src: string;
36
+ readonly request?: RequestPolicy;
37
+ }
38
+ export interface SubtitleTrack extends PlayerTrack, SubtitleResource {
39
+ readonly format: "vtt" | "srt" | "ass" | "ssa" | "pgs" | "vobsub";
40
+ readonly fonts?: readonly SubtitleResource[];
41
+ }
42
+ export interface CaptionStyle {
43
+ readonly fontScale: number;
44
+ readonly color: string;
45
+ readonly background: string;
46
+ readonly edge: "none" | "outline" | "shadow";
47
+ /** Rounded UI card for ordinary text cues; headless and authored layouts stay native. */
48
+ readonly presentation?: "native" | "card";
49
+ }
50
+ export interface CaptionState {
51
+ readonly delaySeconds: number;
52
+ readonly style: CaptionStyle;
53
+ readonly renderer: "native" | "ass" | null;
54
+ readonly status: "off" | "loading" | "ready" | "error";
55
+ readonly warning: "ass-style" | "video-only" | "host-rendered" | null;
56
+ }
57
+ export type AutoCaptionMode = "off" | "original" | "ru";
58
+ /** Web Component presentation; recognition remains in snapshot.autoCaptions. */
59
+ export interface AutoCaptionPresentation {
60
+ readonly mode: AutoCaptionMode;
61
+ readonly loading: boolean;
62
+ readonly progress: number | null;
63
+ readonly error: string | null;
64
+ readonly videoOnly: boolean;
65
+ }
66
+ export interface AutoCaptionState {
67
+ readonly available: boolean;
68
+ readonly reason: "source" | "tracks" | "browser" | null;
69
+ readonly status: "off" | "loading" | "listening" | "error";
70
+ readonly progress: number | null;
71
+ readonly error: string | null;
72
+ readonly backend: "cpu" | "gpu" | null;
73
+ /** Age of the oldest audio in the latest hypothesis; not end-of-phrase delay. */
74
+ readonly latencySeconds: number | null;
75
+ }
76
+ export interface PlayerSource {
77
+ readonly id: string;
78
+ readonly revision: string;
79
+ readonly kind: "mp4" | "hls";
80
+ readonly src: string;
81
+ readonly title?: string;
82
+ readonly metadata?: {
83
+ readonly artist?: string;
84
+ readonly album?: string;
85
+ readonly artwork?: readonly {
86
+ readonly src: string;
87
+ readonly sizes?: string;
88
+ readonly type?: string;
89
+ }[];
90
+ };
91
+ readonly chapters?: readonly PlayerChapter[];
92
+ readonly timeline?: PlayerTimeline;
93
+ readonly audioTracks?: readonly PlayerTrack[];
94
+ readonly textTracks?: readonly PlayerTrack[];
95
+ /** Sidecar times are absolute. Snapshot IDs are prefixed with `subtitle:`. */
96
+ readonly subtitles?: readonly SubtitleTrack[];
97
+ readonly qualities?: readonly PlayerTrack[];
98
+ readonly capabilities?: {
99
+ readonly seek?: "none" | "local" | "host";
100
+ readonly audio?: Controller;
101
+ readonly text?: Controller;
102
+ readonly quality?: Controller;
103
+ readonly playbackRates?: readonly number[];
104
+ };
105
+ readonly request?: RequestPolicy;
106
+ /** Auto prefers MSE, falling back to native HLS; strict request policies require MSE. */
107
+ readonly hlsPlayback?: "auto" | "native" | "mse";
108
+ }
109
+ export interface PlayerCapabilities {
110
+ readonly fullscreen: boolean;
111
+ readonly pictureInPicture: boolean;
112
+ readonly wakeLock: boolean;
113
+ readonly mediaSession: boolean;
114
+ readonly orientation: boolean;
115
+ readonly preservesPitch: boolean;
116
+ readonly seek: "none" | "local" | "host";
117
+ readonly audio: Controller;
118
+ readonly text: Controller;
119
+ readonly quality: Controller;
120
+ readonly autoQuality: boolean;
121
+ readonly playbackRates: readonly number[];
122
+ readonly volume: boolean;
123
+ readonly muted: boolean;
124
+ readonly reasons: Readonly<Partial<Record<"seek" | "audio" | "text" | "quality" | PlatformFeature, string>>>;
125
+ }
126
+ export interface PlayerError {
127
+ readonly code: "autoplay-blocked" | "unsupported" | "invalid-source" | "invalid-command" | "network" | "media" | "host" | "timeout";
128
+ readonly message: string;
129
+ readonly stage: "source" | "play" | "seek" | "audio" | "text" | "quality" | "media";
130
+ readonly retryable: boolean;
131
+ }
132
+ export interface PlayerSelection {
133
+ readonly audioId: string | null;
134
+ readonly textId: string | null;
135
+ readonly qualityId: string | null;
136
+ }
137
+ export interface PlayerSnapshot {
138
+ readonly platform: PlatformState;
139
+ readonly preferences: {
140
+ readonly status: "idle" | "loading" | "ready" | "saving" | "error";
141
+ readonly error: string | null;
142
+ };
143
+ readonly navigation: {
144
+ readonly previous: boolean;
145
+ readonly next: boolean;
146
+ readonly pending: "previous" | "next" | null;
147
+ readonly error: string | null;
148
+ };
149
+ readonly chapters: readonly PlayerChapter[];
150
+ readonly preservesPitch: boolean;
151
+ readonly source: Readonly<Pick<PlayerSource, "id" | "revision" | "kind">> | null;
152
+ readonly playback: Playback;
153
+ readonly intent: Intent;
154
+ readonly pending: {
155
+ readonly type: "source" | CommandType;
156
+ readonly commandId: number;
157
+ } | null;
158
+ readonly positionSeconds: number | null;
159
+ readonly durationSeconds: number | null;
160
+ readonly buffered: readonly TimeRange[];
161
+ readonly seekable: readonly TimeRange[];
162
+ readonly audioTracks: readonly PlayerTrack[];
163
+ readonly textTracks: readonly PlayerTrack[];
164
+ readonly qualities: readonly PlayerTrack[];
165
+ readonly selection: PlayerSelection;
166
+ readonly volume: number;
167
+ readonly muted: boolean;
168
+ readonly playbackRate: number;
169
+ readonly captions: CaptionState;
170
+ readonly autoCaptions: AutoCaptionState;
171
+ readonly capabilities: PlayerCapabilities;
172
+ readonly error: PlayerError | null;
173
+ }
174
+ export interface HostTarget {
175
+ readonly positionSeconds?: number;
176
+ readonly audioId?: string;
177
+ readonly textId?: string | null;
178
+ readonly qualityId?: string;
179
+ }
180
+ export type HostCommand = ({
181
+ readonly type: "seek";
182
+ readonly seconds: number;
183
+ } | {
184
+ readonly type: "audio";
185
+ readonly id: string;
186
+ } | {
187
+ readonly type: "text";
188
+ readonly id: string | null;
189
+ } | {
190
+ readonly type: "quality";
191
+ readonly id: string;
192
+ }) & {
193
+ readonly target: HostTarget;
194
+ };
195
+ export interface HostContext {
196
+ readonly sourceId: string;
197
+ readonly revision: string;
198
+ readonly commandId: number;
199
+ readonly signal: AbortSignal;
200
+ readonly positionSeconds: number | null;
201
+ /** Getter: play/pause during preparation is visible without starting another mutation. */
202
+ readonly intent: Intent;
203
+ }
204
+ export interface HostResult {
205
+ readonly source?: PlayerSource;
206
+ /** Confirmation from host, not admission. Replacement still waits for media readiness. */
207
+ readonly selection?: Partial<PlayerSelection>;
208
+ }
209
+ export interface HostAdapter {
210
+ execute(command: HostCommand, context: HostContext): Promise<HostResult>;
211
+ }
212
+ export type PlayerEventType = "statechange" | "positionchange" | "trackchange" | "qualitychange" | "commandchange" | "capabilitieschange" | "ended" | "error";
213
+ export interface PlayerEvent {
214
+ readonly type: PlayerEventType;
215
+ readonly source: PlayerSnapshot["source"];
216
+ readonly snapshot: PlayerSnapshot;
217
+ }
218
+ export interface PlayerOptions {
219
+ readonly media: HTMLVideoElement;
220
+ readonly container?: HTMLElement;
221
+ readonly platform?: PlatformOptions;
222
+ readonly preferences?: PreferencesAdapter;
223
+ readonly navigation?: PlayerNavigation;
224
+ readonly thumbnail?: ThumbnailResolver;
225
+ readonly host?: HostAdapter;
226
+ readonly onEvent?: (event: PlayerEvent) => void;
227
+ /** Bounds media readiness/confirmation. Host owns reconciliation of accepted mutations. */
228
+ readonly commandTimeoutMs?: number;
229
+ }
230
+ export interface SourceOptions {
231
+ readonly positionSeconds?: number;
232
+ readonly intent?: Intent;
233
+ readonly trackPreferences?: TrackPreferences;
234
+ }
235
+ /** Per-source initial choices. Hosts own persistence; IDs never become preferences. */
236
+ export interface TrackPreferences {
237
+ readonly audioLanguages?: readonly string[];
238
+ /** Omitted/null means Off, including DEFAULT/FORCED tracks. */
239
+ readonly text?: {
240
+ readonly languages?: readonly string[];
241
+ readonly forced?: boolean;
242
+ readonly sdh?: boolean;
243
+ } | null;
244
+ }
245
+ export interface PlayerRuntime {
246
+ getSnapshot(): PlayerSnapshot;
247
+ subscribe(listener: () => void): () => void;
248
+ setSource(source: PlayerSource, options?: SourceOptions): Promise<void>;
249
+ play(): Promise<void>;
250
+ pause(): Promise<void>;
251
+ seek(seconds: number): Promise<void>;
252
+ selectAudio(id: string): Promise<void>;
253
+ selectText(id: string | null): Promise<void>;
254
+ /** Read locally without uploading. Replaces the previous local subtitle file. */
255
+ loadSubtitles(file: File): Promise<void>;
256
+ removeLocalSubtitles(): Promise<void>;
257
+ setCaptionStyle(style: Partial<CaptionStyle>): Promise<void>;
258
+ setCaptionDelay(seconds: number): Promise<void>;
259
+ /** Local recognition, only when no prepared subtitles are available. Off on source change. */
260
+ setAutoCaptions(enabled: boolean): Promise<void>;
261
+ selectQuality(id: string): Promise<void>;
262
+ setVolume(value: number): Promise<void>;
263
+ setMuted(value: boolean): Promise<void>;
264
+ setPlaybackRate(value: number): Promise<void>;
265
+ setPreservesPitch(value: boolean): Promise<void>;
266
+ enterFullscreen(): Promise<void>;
267
+ exitFullscreen(): Promise<void>;
268
+ requestPictureInPicture(): Promise<void>;
269
+ exitPictureInPicture(): Promise<void>;
270
+ setPlatformOptions(options: PlatformOptions): void;
271
+ setPreferences(adapter?: PreferencesAdapter): Promise<void>;
272
+ setNavigation(navigation: PlayerNavigation): void;
273
+ navigate(direction: "previous" | "next"): Promise<void>;
274
+ setThumbnailResolver(resolver?: ThumbnailResolver): void;
275
+ getThumbnail(seconds: number, signal: AbortSignal): Promise<PlayerThumbnail | null>;
276
+ dispose(): void;
277
+ }
278
+ export type PlatformFeature = "fullscreen" | "pictureInPicture" | "wakeLock" | "mediaSession" | "orientation";
279
+ export interface PlatformOptions {
280
+ readonly wakeLock?: boolean;
281
+ readonly mediaSession?: boolean;
282
+ /** Optional fullscreen-only lock. Never required for successful fullscreen. */
283
+ readonly orientation?: "landscape";
284
+ }
285
+ export interface PlatformState {
286
+ readonly fullscreen: boolean;
287
+ readonly pictureInPicture: boolean;
288
+ readonly wakeLock: boolean;
289
+ readonly orientationLocked: boolean;
290
+ readonly mediaSessionActive: boolean;
291
+ readonly pending: "fullscreen" | "pictureInPicture" | null;
292
+ readonly error: {
293
+ readonly feature: PlatformFeature;
294
+ readonly message: string;
295
+ } | null;
296
+ }
297
+ export interface PlayerPreferences {
298
+ readonly volume?: number;
299
+ readonly muted?: boolean;
300
+ readonly playbackRate?: number;
301
+ readonly preservesPitch?: boolean;
302
+ readonly tracks?: TrackPreferences;
303
+ readonly captionStyle?: Partial<CaptionStyle>;
304
+ readonly captionDelaySeconds?: number;
305
+ }
306
+ export interface PreferencesAdapter {
307
+ load(context: {
308
+ readonly signal: AbortSignal;
309
+ }): Promise<PlayerPreferences | null>;
310
+ save(preferences: PlayerPreferences, context: {
311
+ readonly signal: AbortSignal;
312
+ }): Promise<void>;
313
+ }
314
+ export interface PlayerNavigation {
315
+ readonly previous?: () => void | Promise<void>;
316
+ readonly next?: () => void | Promise<void>;
317
+ /** Only an actual ended event invokes next automatically. Defaults to false. */
318
+ readonly autoAdvance?: boolean;
319
+ }
320
+ export interface PlayerChapter {
321
+ readonly startSeconds: number;
322
+ readonly title: string;
323
+ }
324
+ export interface PlayerThumbnail {
325
+ readonly src: string;
326
+ }
327
+ export type ThumbnailResolver = (context: {
328
+ readonly source: NonNullable<PlayerSnapshot["source"]>;
329
+ readonly seconds: number;
330
+ readonly signal: AbortSignal;
331
+ }) => Promise<PlayerThumbnail | null>;