@ngxsp/core 0.0.1-next.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.
package/index.d.ts ADDED
@@ -0,0 +1,853 @@
1
+ import { Observable } from 'rxjs';
2
+
3
+ interface MetadataHint {
4
+ contentId?: string;
5
+ title?: string;
6
+ year?: number;
7
+ imdbId?: string;
8
+ tmdbId?: string;
9
+ }
10
+ interface VideoMetadata {
11
+ title?: string;
12
+ description?: string;
13
+ year?: number;
14
+ genres?: string[];
15
+ cast?: string[];
16
+ posterUrl?: string;
17
+ backdropUrl?: string;
18
+ extras?: Record<string, unknown>;
19
+ }
20
+ interface MetadataMatch {
21
+ id: string;
22
+ label: string;
23
+ score?: number;
24
+ extra?: unknown;
25
+ }
26
+ interface MetadataProvider {
27
+ id: string;
28
+ match(hint: MetadataHint): Promise<MetadataMatch[]>;
29
+ fetch(id: string): Promise<VideoMetadata>;
30
+ }
31
+
32
+ interface CapabilityReport {
33
+ timestamp: number;
34
+ userAgent?: string;
35
+ source?: {
36
+ type?: string;
37
+ live?: boolean;
38
+ drm?: boolean;
39
+ url?: string;
40
+ contentId?: string;
41
+ };
42
+ platform: {
43
+ isSecureContext: boolean;
44
+ };
45
+ features: {
46
+ mse: boolean;
47
+ eme: boolean;
48
+ mediaCapabilities: boolean;
49
+ };
50
+ canPlay: Record<string, boolean>;
51
+ /**
52
+ * Optional, best-effort decoding probes via the MediaCapabilities API.
53
+ *
54
+ * Keys are stable labels (e.g. "h264_1080p").
55
+ */
56
+ mediaDecoding?: Record<string, {
57
+ supported: boolean;
58
+ smooth?: boolean;
59
+ powerEfficient?: boolean;
60
+ }>;
61
+ notes: string[];
62
+ }
63
+ /** Best-effort decoding probes used for diagnostics and ABR tuning hints. */
64
+ declare function createMediaDecodingReport(): Promise<CapabilityReport['mediaDecoding']>;
65
+ declare function createCapabilityReport(videoEl?: HTMLVideoElement, cfg?: PlayerConfig, mediaDecoding?: CapabilityReport['mediaDecoding']): CapabilityReport;
66
+
67
+ type SourceType = 'local-file' | 'url' | 'hls' | 'dash';
68
+ /**
69
+ * High-level playback profiles (v2.0).
70
+ *
71
+ * Used to apply sensible defaults (ABR / buffering) with minimal configuration.
72
+ */
73
+ type EngineProfileId = 'default' | 'tv' | 'mobile' | 'low-latency';
74
+ type AspectRatioMode = 'contain' | 'cover' | '1:1' | '4:3' | '16:9' | '21:9';
75
+ type ProgressMode = 'local' | 'server' | 'hybrid';
76
+ type AdsMode = 'csai' | 'ssai' | 'mixed';
77
+ interface DrmConfig {
78
+ /**
79
+ * Key system configuration, usually driven by your backend.
80
+ * Examples: 'com.widevine.alpha', 'com.microsoft.playready'
81
+ */
82
+ keySystems: Record<string, {
83
+ licenseServerUrl: string;
84
+ /**
85
+ * Additional request headers sent to the license server.
86
+ */
87
+ headers?: Record<string, string>;
88
+ /** Optional robustness constraints (e.g. 'SW_SECURE_DECODE', 'HW_SECURE_ALL'). */
89
+ videoRobustness?: string;
90
+ audioRobustness?: string;
91
+ }>;
92
+ /**
93
+ * Whether offline/persistent licenses are intended for this content.
94
+ * Actual capability depends on device + license policy.
95
+ */
96
+ persistentLicense?: boolean;
97
+ /**
98
+ * License request retry policy.
99
+ *
100
+ * If unset, engines should apply conservative defaults (3 attempts, exponential backoff).
101
+ */
102
+ licenseRetry?: {
103
+ maxAttempts?: number;
104
+ baseDelayMs?: number;
105
+ backoffFactor?: number;
106
+ fuzzFactor?: number;
107
+ timeoutMs?: number;
108
+ };
109
+ }
110
+ interface PlayerSource {
111
+ type: SourceType;
112
+ url?: string;
113
+ file?: File;
114
+ live?: boolean;
115
+ drm?: DrmConfig;
116
+ /** Optional engine profile for default tuning (can be set at `PlayerConfig.profile`). */
117
+ profile?: EngineProfileId;
118
+ /**
119
+ * Optional ABR hints for adaptive engines (e.g. Shaka).
120
+ *
121
+ * These are *hints* only: engines may ignore them if unsupported.
122
+ */
123
+ abr?: {
124
+ /** Hard cap for rendition height (e.g. 720). */
125
+ maxHeight?: number;
126
+ /** Hard cap for bitrate (bits/s). */
127
+ maxBandwidth?: number;
128
+ };
129
+ /** Internal engine hints (best-effort). */
130
+ _shakaStreaming?: {
131
+ lowLatencyMode?: boolean;
132
+ bufferingGoalSec?: number;
133
+ rebufferingGoalSec?: number;
134
+ };
135
+ /**
136
+ * Whether this source is allowed to be downloaded/offlined.
137
+ */
138
+ offlineEligible?: boolean;
139
+ /**
140
+ * Optional identifier for progress/metadata/subtitles lookup.
141
+ */
142
+ contentId?: string;
143
+ /**
144
+ * Optional sidecar URL for local metadata (json/txt) or subtitles.
145
+ */
146
+ sidecar?: {
147
+ metadataUrl?: string;
148
+ subtitles?: Array<{
149
+ lang: string;
150
+ url: string;
151
+ format?: string;
152
+ }>;
153
+ chaptersUrl?: string;
154
+ thumbnailVttUrl?: string;
155
+ };
156
+ }
157
+ interface ProgressConfig {
158
+ mode: ProgressMode;
159
+ endpoint?: string;
160
+ intervalSec: number;
161
+ }
162
+ interface SkipSegment {
163
+ kind: 'intro' | 'recap';
164
+ startSec: number;
165
+ endSec: number;
166
+ }
167
+ interface NextEpisodeHint {
168
+ label: string;
169
+ onNext: () => void | Promise<void>;
170
+ }
171
+ interface UiConfig {
172
+ /** Optional UI skin preset (default: classic) */
173
+ skin?: 'classic' | 'netflix';
174
+ /** Optional display title used by some skins */
175
+ title?: string;
176
+ ratios: AspectRatioMode[];
177
+ defaultRatio: AspectRatioMode;
178
+ keyboardShortcuts: boolean;
179
+ skipSegments?: SkipSegment[];
180
+ nextEpisode?: NextEpisodeHint;
181
+ chaptersVttUrl?: string;
182
+ thumbnailsVttUrl?: string;
183
+ /** Preview strategy for timeline hover (default: thumbnails) */
184
+ previewMode?: 'thumbnails' | 'frame' | 'off';
185
+ }
186
+ interface ResilienceConfig {
187
+ retries: number;
188
+ backoffMs: {
189
+ min: number;
190
+ max: number;
191
+ };
192
+ healthEndpoint?: string;
193
+ }
194
+ interface SubtitleHint {
195
+ contentId?: string;
196
+ title?: string;
197
+ season?: number;
198
+ episode?: number;
199
+ language?: string;
200
+ /** Année (films) */
201
+ year?: number;
202
+ /** Identifiants externes (optionnels) */
203
+ imdbId?: string;
204
+ tmdbId?: number;
205
+ /** Pour rester extensible sans casser le typage */
206
+ extra?: Record<string, unknown>;
207
+ }
208
+ interface SubtitlesConfig {
209
+ enabled: boolean;
210
+ providers: string[];
211
+ preferredLang?: string;
212
+ /** Optional hint for internet providers */
213
+ hint?: SubtitleHint;
214
+ }
215
+ interface MetadataConfig {
216
+ enabled: boolean;
217
+ providers: string[];
218
+ localSidecar: boolean;
219
+ /** Optional hint for internet providers */
220
+ hint?: MetadataHint;
221
+ }
222
+ interface AdsConfig {
223
+ enabled: boolean;
224
+ mode: AdsMode;
225
+ providerConfig?: unknown;
226
+ }
227
+ interface EpisodeItem {
228
+ /** Unique id for progress/offline/metadata/subtitles. */
229
+ contentId: string;
230
+ season?: number;
231
+ episode?: number;
232
+ title?: string;
233
+ source: PlayerSource;
234
+ /** Optional per-episode UI overrides (skip segments, thumbnails, etc.) */
235
+ ui?: Partial<UiConfig>;
236
+ /** Optional metadata hint for internet providers. */
237
+ metadataHint?: {
238
+ title?: string;
239
+ year?: number;
240
+ imdbId?: string;
241
+ tmdbId?: string;
242
+ };
243
+ }
244
+ interface SeriesConfig {
245
+ title?: string;
246
+ /** 0-based index of current episode in episodes[] */
247
+ currentIndex: number;
248
+ episodes: EpisodeItem[];
249
+ /** Auto-play next episode when ended. Default true. */
250
+ autoNext?: boolean;
251
+ /** Warm-up next episode manifest when within N seconds of end. Default 30. */
252
+ preloadNextSeconds?: number;
253
+ }
254
+ interface PlayerConfig {
255
+ source: PlayerSource;
256
+ /** High-level playback profile (v2.0). */
257
+ profile?: EngineProfileId;
258
+ /** Optional DRM config. When set, it overrides/augments source.drm. */
259
+ drm?: DrmConfig;
260
+ /** Optional series mode */
261
+ series?: SeriesConfig;
262
+ /** Optional playlist mode (overrides source at runtime) */
263
+ playlist?: PlayerSource[];
264
+ /** Starting index in playlist (default 0) */
265
+ playlistStartIndex?: number;
266
+ /** Auto-play next item on ended (default true when playlist provided) */
267
+ autoNext?: boolean;
268
+ progress: ProgressConfig;
269
+ ui: UiConfig;
270
+ resilience: ResilienceConfig;
271
+ subtitles: SubtitlesConfig;
272
+ metadata: MetadataConfig;
273
+ ads: AdsConfig;
274
+ /** Optional: enable offline materialization via @ngxsp/plugin-offline. */
275
+ offline?: {
276
+ enabled?: boolean;
277
+ /** Storage key override; default = `offline:${contentId || url}` */
278
+ key?: string;
279
+ };
280
+ /**
281
+ * Remote control-plane: fetch JSON overrides at runtime (optionally cached), then merge into this config.
282
+ * Designed to keep the engine decoupled from Angular and allow feature flags / tuning without rebuild.
283
+ */
284
+ remoteConfig?: RemoteConfigConfig;
285
+ }
286
+ interface RemoteConfigConfig {
287
+ enabled: boolean;
288
+ /** Absolute URL returning a JSON payload (object). */
289
+ url: string;
290
+ /** Cache TTL (seconds). Default: 300s. */
291
+ ttlSec?: number;
292
+ /** Merge mode. Default: 'deep'. */
293
+ mode?: 'shallow' | 'deep';
294
+ /** Allow-list of top-level keys that can be overridden remotely. */
295
+ allow?: string[];
296
+ }
297
+ type RemoteConfigPayload = Record<string, unknown>;
298
+ type PlayerState = {
299
+ kind: 'idle';
300
+ } | {
301
+ kind: 'loading';
302
+ } | {
303
+ kind: 'buffering';
304
+ } | {
305
+ kind: 'playing';
306
+ } | {
307
+ kind: 'paused';
308
+ } | {
309
+ kind: 'ended';
310
+ } | {
311
+ kind: 'error';
312
+ error: PlayerError;
313
+ };
314
+ /**
315
+ * Minimal time model exposed by the public API.
316
+ * Kept as a stable alias for wrappers (Angular/Electron) and adapters.
317
+ */
318
+ interface PlayerTime {
319
+ currentTime: number;
320
+ duration: number;
321
+ /** Best-effort buffered end (seconds) for native playback. */
322
+ bufferedEnd?: number;
323
+ }
324
+
325
+ interface DiagnosticsEngineInfo {
326
+ /** Selected engine id (ex: 'native', 'shaka') */
327
+ id: string;
328
+ /** How the engine was selected */
329
+ selectedBy: 'supports' | 'caps' | 'fallback' | 'explicit' | 'none';
330
+ /** Optional selection reasons (debuggable, non-normative) */
331
+ reasons: string[];
332
+ }
333
+ interface DiagnosticsSnapshot {
334
+ /** Epoch milliseconds when the snapshot was produced. */
335
+ ts: number;
336
+ engine: DiagnosticsEngineInfo;
337
+ capabilities: CapabilityReport;
338
+ source?: PlayerSource;
339
+ state: PlayerState;
340
+ time: PlayerTime;
341
+ lastNonFatal: PlayerError | null;
342
+ /** @deprecated use `engine.id` */
343
+ selectedEngineId?: string;
344
+ /** @deprecated use `state` + `time` */
345
+ snapshot?: {
346
+ currentTime: number;
347
+ duration: number;
348
+ state: PlayerState;
349
+ bufferedAheadSec: number;
350
+ };
351
+ }
352
+ type PlayerErrorCode = 'AUTOPLAY_BLOCKED' | 'SRC_NOT_SUPPORTED' | 'NETWORK_TIMEOUT' | 'NETWORK_HTTP' | 'DECODE_ERROR' | 'ENGINE_MISSING' | 'ENGINE_CRASH' | 'DRM_ERROR' | 'UNKNOWN';
353
+ interface PlayerError {
354
+ code: PlayerErrorCode | string;
355
+ message: string;
356
+ fatal?: boolean;
357
+ details?: unknown;
358
+ }
359
+ interface MediaTrack {
360
+ id: string;
361
+ kind: 'audio' | 'text' | 'video';
362
+ language?: string;
363
+ label?: string;
364
+ active: boolean;
365
+ width?: number;
366
+ height?: number;
367
+ bandwidth?: number;
368
+ }
369
+ type PlayerEvent = {
370
+ type: 'play';
371
+ } | {
372
+ type: 'pause';
373
+ } | {
374
+ type: 'timeupdate';
375
+ currentTime: number;
376
+ duration: number;
377
+ } | {
378
+ type: 'ended';
379
+ } | {
380
+ type: 'error';
381
+ code: PlayerErrorCode | string;
382
+ message: string;
383
+ detail?: unknown;
384
+ } | {
385
+ type: 'adbreak-start';
386
+ id?: string;
387
+ } | {
388
+ type: 'adbreak-end';
389
+ id?: string;
390
+ } | {
391
+ type: 'ad-start';
392
+ id?: string;
393
+ } | {
394
+ type: 'ad-end';
395
+ id?: string;
396
+ };
397
+
398
+ type EngineEvent = {
399
+ type: 'timeupdate';
400
+ currentTime: number;
401
+ duration: number;
402
+ } | {
403
+ type: 'bufferupdate';
404
+ bufferedAheadSec: number;
405
+ } | {
406
+ type: 'state';
407
+ state: 'loading' | 'buffering' | 'playing' | 'paused' | 'ended';
408
+ } | {
409
+ type: 'tracks';
410
+ tracks: MediaTrack[];
411
+ } | {
412
+ type: 'error';
413
+ error: {
414
+ code: string;
415
+ message: string;
416
+ fatal?: boolean;
417
+ details?: unknown;
418
+ };
419
+ };
420
+ interface LoadOptions {
421
+ startTimeSec?: number;
422
+ /** Optional playlist index override for PlayerFacade */
423
+ playlistIndex?: number;
424
+ }
425
+ interface PlaybackEngine {
426
+ readonly id: string;
427
+ attach(videoEl: HTMLVideoElement): Promise<void>;
428
+ load(source: PlayerSource, opts?: LoadOptions): Promise<void>;
429
+ destroy(): Promise<void>;
430
+ play(): Promise<void>;
431
+ pause(): Promise<void>;
432
+ seekTo(timeSec: number): Promise<void>;
433
+ setPlaybackRate(rate: number): Promise<void>;
434
+ setVolume(volume: number): Promise<void>;
435
+ setTextTrack(trackId: string | null): Promise<void>;
436
+ setAudioTrack(trackId: string | null): Promise<void>;
437
+ setVideoTrack(trackId: string | null): Promise<void>;
438
+ getTracks(): Promise<MediaTrack[]>;
439
+ onEvent(cb: (ev: EngineEvent) => void): () => void;
440
+ }
441
+
442
+ interface PlaybackCapabilities {
443
+ /** Browser has MediaSource (MSE). */
444
+ mse: boolean;
445
+ /** Browser has Encrypted Media Extensions (EME) API. */
446
+ eme: boolean;
447
+ /** True if navigator.mediaCapabilities is available. */
448
+ mediaCapabilities: boolean;
449
+ /** Whether basic HLS playback is possible via MSE/native. */
450
+ hls: boolean;
451
+ /** Whether basic DASH playback is possible via MSE. */
452
+ dash: boolean;
453
+ /** Progressive mp4/webm playback via <video src>. */
454
+ progressive: boolean;
455
+ /** Heuristic: support for at least one of common video codecs. */
456
+ codecs: {
457
+ mp4_h264: boolean;
458
+ mp4_hevc: boolean;
459
+ webm_vp9: boolean;
460
+ webm_av1: boolean;
461
+ };
462
+ /** Known DRM key systems availability (best-effort). */
463
+ drm: {
464
+ widevine: boolean;
465
+ playready: boolean;
466
+ fairplay: boolean;
467
+ };
468
+ }
469
+ declare function createDefaultCapabilities(): PlaybackCapabilities;
470
+ declare function detectPlaybackCapabilities(videoEl?: HTMLVideoElement): Promise<PlaybackCapabilities>;
471
+
472
+ interface EngineFactory {
473
+ /** Unique id (ex: 'native', 'shaka') */
474
+ id: string;
475
+ /** Higher priority wins when multiple support the same source. Default 0. */
476
+ priority?: number;
477
+ /** Whether this engine supports this config/source. */
478
+ supports(config: PlayerConfig): boolean;
479
+ /**
480
+ * Optional, capability-aware selector.
481
+ * If present, it is preferred over supports().
482
+ */
483
+ supportsWithCaps?: (config: PlayerConfig, caps: PlaybackCapabilities) => boolean;
484
+ /** Create a new engine instance. */
485
+ create(): PlaybackEngine;
486
+ }
487
+ declare function selectEngine(config: PlayerConfig, factories: EngineFactory[], caps?: PlaybackCapabilities): EngineFactory | null;
488
+
489
+ interface HttpPort {
490
+ fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
491
+ }
492
+
493
+ interface StoragePort {
494
+ get(key: string): Promise<string | null>;
495
+ set(key: string, value: string): Promise<void>;
496
+ remove?(key: string): Promise<void>;
497
+ }
498
+ declare class MemoryStoragePort implements StoragePort {
499
+ private map;
500
+ get(key: string): Promise<string | null>;
501
+ set(key: string, value: string): Promise<void>;
502
+ remove(key: string): Promise<void>;
503
+ }
504
+
505
+ interface TelemetryPort {
506
+ add(sample: Record<string, unknown>): void;
507
+ export?(): unknown;
508
+ }
509
+ declare class NoopTelemetryPort implements TelemetryPort {
510
+ add(_sample: Record<string, unknown>): void;
511
+ }
512
+
513
+ interface LogPort {
514
+ debug(...args: unknown[]): void;
515
+ info(...args: unknown[]): void;
516
+ warn(...args: unknown[]): void;
517
+ error(...args: unknown[]): void;
518
+ }
519
+ declare class ConsoleLogPort implements LogPort {
520
+ debug(...args: unknown[]): void;
521
+ info(...args: unknown[]): void;
522
+ warn(...args: unknown[]): void;
523
+ error(...args: unknown[]): void;
524
+ }
525
+
526
+ interface PlayerPorts {
527
+ http: HttpPort;
528
+ storage: StoragePort;
529
+ telemetry: TelemetryPort;
530
+ log: LogPort;
531
+ }
532
+ declare function createDefaultPorts(): PlayerPorts;
533
+
534
+ interface PlayerSnapshot {
535
+ currentTime: number;
536
+ duration: number;
537
+ state: PlayerState;
538
+ bufferedAheadSec: number;
539
+ }
540
+ /**
541
+ * Public diagnostics payload.
542
+ *
543
+ * NOTE: This is intentionally an alias of `DiagnosticsSnapshot` to keep one
544
+ * canonical shape exported by `@ngxsp/core`.
545
+ */
546
+ type PlayerDiagnostics = DiagnosticsSnapshot;
547
+ declare class PlayerFacade {
548
+ private chooseEngine;
549
+ private ports;
550
+ private engineFactories;
551
+ private engineId;
552
+ private lastNonFatal;
553
+ private mediaDecodingSub;
554
+ private video?;
555
+ private engine?;
556
+ private plugins;
557
+ private ctx?;
558
+ private stateSub;
559
+ private timeSub;
560
+ private bufferSub;
561
+ private tracksSub;
562
+ private errorSub;
563
+ private metadataSub;
564
+ private capsSub;
565
+ readonly capabilities$: Observable<PlaybackCapabilities | null>;
566
+ readonly state$: Observable<PlayerState>;
567
+ readonly time$: Observable<{
568
+ currentTime: number;
569
+ duration: number;
570
+ }>;
571
+ readonly bufferAheadSec$: Observable<number>;
572
+ readonly tracks$: Observable<MediaTrack[]>;
573
+ readonly error$: Observable<PlayerError>;
574
+ readonly metadata$: Observable<VideoMetadata | null>;
575
+ private seriesIndexSub;
576
+ readonly seriesIndex$: Observable<number | null>;
577
+ private playlistIndexSub;
578
+ readonly playlistIndex$: Observable<number | null>;
579
+ private playlist;
580
+ private playlistKey;
581
+ private externalVttUrl?;
582
+ constructor(chooseEngine?: (cfg: PlayerConfig) => PlaybackEngine, ports?: PlayerPorts);
583
+ attach(videoEl: HTMLVideoElement, plugins: PlayerPlugin[] | undefined, config: PlayerConfig, ports?: Partial<PlayerPorts>, engines?: EngineFactory[]): void;
584
+ load(config: PlayerConfig, opts?: LoadOptions): Promise<void>;
585
+ reload(opts?: LoadOptions): Promise<void>;
586
+ next(): Promise<void>;
587
+ previous(): Promise<void>;
588
+ destroy(): Promise<void>;
589
+ /** Emit a non-fatal error (does not transition to fatal error state). */
590
+ emitNonFatal(err: unknown, code?: PlayerErrorCode | string): void;
591
+ /** Emit a fatal error (transitions to error state). */
592
+ private emitFatal;
593
+ private tryAutoNext;
594
+ private installed;
595
+ private qoe;
596
+ private installVideoEventListeners;
597
+ private emitTelemetry;
598
+ private toPlayerError;
599
+ play(): Promise<void>;
600
+ pause(): Promise<void>;
601
+ seekTo(timeSec: number): Promise<void>;
602
+ setRate(rate: number): Promise<void>;
603
+ setVolume(volume: number): Promise<void>;
604
+ setTextTrack(trackId: string | null): Promise<void>;
605
+ setAudioTrack(trackId: string | null): Promise<void>;
606
+ setVideoTrack(trackId: string | null): Promise<void>;
607
+ setAspectRatio(_mode: AspectRatioMode): void;
608
+ snapshot(): PlayerSnapshot;
609
+ /**
610
+ * Diagnostics helper for UI / support logs.
611
+ * Safe to call anytime after attach().
612
+ */
613
+ diagnostics(): PlayerDiagnostics;
614
+ setExternalVttTrack(lang: string, vtt: string): Promise<void>;
615
+ setMetadata(meta: VideoMetadata): void;
616
+ private ensureExternalTrack;
617
+ private handleEngineEvent;
618
+ private tryRestoreStartTime;
619
+ }
620
+
621
+ interface PlayerContext {
622
+ config: PlayerConfig;
623
+ facade: PlayerFacade;
624
+ ports: PlayerPorts;
625
+ /**
626
+ * Stable content identifier if available (used for progress/subtitle/metadata lookups).
627
+ */
628
+ contentId?: string;
629
+ }
630
+ interface PlayerPlugin {
631
+ id: string;
632
+ onInit?(ctx: PlayerContext): void | Promise<void>;
633
+ onLoadStart?(ctx: PlayerContext): void | Promise<void>;
634
+ onLoaded?(ctx: PlayerContext): void | Promise<void>;
635
+ onError?(ctx: PlayerContext, err: PlayerError): void | Promise<void>;
636
+ onDestroy?(ctx: PlayerContext): void | Promise<void>;
637
+ }
638
+
639
+ declare class NativeEngine implements PlaybackEngine {
640
+ readonly id = "native";
641
+ private video?;
642
+ private listeners;
643
+ attach(videoEl: HTMLVideoElement): Promise<void>;
644
+ load(source: PlayerSource, opts?: {
645
+ startTimeSec?: number;
646
+ }): Promise<void>;
647
+ destroy(): Promise<void>;
648
+ play(): Promise<void>;
649
+ pause(): Promise<void>;
650
+ seekTo(timeSec: number): Promise<void>;
651
+ setPlaybackRate(rate: number): Promise<void>;
652
+ setVolume(volume: number): Promise<void>;
653
+ setTextTrack(_trackId: string | null): Promise<void>;
654
+ setAudioTrack(_trackId: string | null): Promise<void>;
655
+ setVideoTrack(_trackId: string | null): Promise<void>;
656
+ getTracks(): Promise<MediaTrack[]>;
657
+ onEvent(cb: (ev: EngineEvent) => void): () => void;
658
+ }
659
+
660
+ /**
661
+ * Local provider: loads sidecar metadata from a URL (json or txt).
662
+ * - JSON: parsed into `VideoMetadata` where possible
663
+ * - TXT: stored as description
664
+ */
665
+ declare class LocalMetadataProvider implements MetadataProvider {
666
+ id: string;
667
+ match(_hint: MetadataHint): Promise<MetadataMatch[]>;
668
+ fetch(id: string): Promise<VideoMetadata>;
669
+ }
670
+
671
+ interface SubtitleQuery {
672
+ contentId?: string;
673
+ title?: string;
674
+ season?: number;
675
+ episode?: number;
676
+ language?: string;
677
+ }
678
+ interface SubtitleCandidate {
679
+ id: string;
680
+ providerId: string;
681
+ lang: string;
682
+ label?: string;
683
+ format?: string;
684
+ score?: number;
685
+ url?: string;
686
+ extra?: unknown;
687
+ }
688
+ interface SubtitleProvider {
689
+ id: string;
690
+ search(query: SubtitleQuery): Promise<SubtitleCandidate[]>;
691
+ fetch(candidate: SubtitleCandidate): Promise<string | ArrayBuffer>;
692
+ }
693
+
694
+ /**
695
+ * Local provider: uses `config.source.sidecar.subtitles` entries as candidates.
696
+ */
697
+ declare class LocalSubtitleProvider implements SubtitleProvider {
698
+ id: string;
699
+ search(query: SubtitleQuery): Promise<SubtitleCandidate[]>;
700
+ fetch(candidate: SubtitleCandidate): Promise<string | ArrayBuffer>;
701
+ }
702
+
703
+ interface CacheStore {
704
+ get<T>(key: string): Promise<T | null>;
705
+ set<T>(key: string, value: T, ttlMs: number): Promise<void>;
706
+ del(key: string): Promise<void>;
707
+ }
708
+ declare class DefaultCacheStore implements CacheStore {
709
+ private dbp;
710
+ get<T>(key: string): Promise<T | null>;
711
+ set<T>(key: string, value: T, ttlMs: number): Promise<void>;
712
+ del(key: string): Promise<void>;
713
+ private db;
714
+ }
715
+
716
+ type PlayerCommand = {
717
+ type: 'play';
718
+ } | {
719
+ type: 'pause';
720
+ } | {
721
+ type: 'toggle';
722
+ } | {
723
+ type: 'seek';
724
+ to: number;
725
+ } | {
726
+ type: 'setPlaybackRate';
727
+ rate: number;
728
+ } | {
729
+ type: 'setVolume';
730
+ volume: number;
731
+ } | {
732
+ type: 'setMuted';
733
+ muted: boolean;
734
+ } | {
735
+ type: 'selectTextTrack';
736
+ id: string;
737
+ } | {
738
+ type: 'disableTextTrack';
739
+ } | {
740
+ type: 'selectAudioTrack';
741
+ id: string;
742
+ } | {
743
+ type: 'requestPiP';
744
+ } | {
745
+ type: 'exitPiP';
746
+ } | {
747
+ type: 'requestFullscreen';
748
+ } | {
749
+ type: 'exitFullscreen';
750
+ } | {
751
+ type: 'requestCast';
752
+ } | {
753
+ type: 'stopCast';
754
+ };
755
+ declare function isCategorizedEventType(type: string): boolean;
756
+
757
+ type Unsubscribe = () => void;
758
+ interface PlayerEventBus {
759
+ emit(event: PlayerEvent): void;
760
+ on<T extends PlayerEvent['type']>(type: T, handler: (e: Extract<PlayerEvent, {
761
+ type: T;
762
+ }>) => void): Unsubscribe;
763
+ }
764
+ declare class SimpleEventBus implements PlayerEventBus {
765
+ private handlers;
766
+ emit(event: PlayerEvent): void;
767
+ on<T extends PlayerEvent['type']>(type: T, handler: (e: Extract<PlayerEvent, {
768
+ type: T;
769
+ }>) => void): Unsubscribe;
770
+ }
771
+
772
+ declare class ProgressPlugin implements PlayerPlugin {
773
+ readonly id = "progress";
774
+ private timer;
775
+ onLoadStart(ctx: PlayerContext): Promise<void>;
776
+ onLoaded(ctx: PlayerContext): Promise<void>;
777
+ onDestroy(ctx: PlayerContext): Promise<void>;
778
+ private stop;
779
+ private save;
780
+ }
781
+
782
+ declare class ResiliencePlugin implements PlayerPlugin {
783
+ readonly id = "resilience";
784
+ private attempts;
785
+ private recovering;
786
+ onLoadStart(ctx: PlayerContext): Promise<void>;
787
+ onError(ctx: PlayerContext, err: PlayerError): Promise<void>;
788
+ private health;
789
+ }
790
+
791
+ /**
792
+ * SSAI helper (v1.11)
793
+ *
794
+ * Keeps the core decoupled from vendor SDKs.
795
+ * This plugin rewrites the manifest URL before the engine loads it.
796
+ */
797
+ interface SsaiManifestRewriteConfig {
798
+ /** If provided, used as the final playback URL (highest priority). */
799
+ stitchedUrl?: string;
800
+ /** Append query parameters to the source URL (e.g. session, device). */
801
+ appendQuery?: Record<string, string | number | boolean | null | undefined>;
802
+ }
803
+ declare function createSsaiManifestRewritePlugin(): PlayerPlugin;
804
+
805
+ type TelemetryLevel = 'debug' | 'info' | 'warn' | 'error';
806
+ type TelemetryEvent = 'player:init' | 'player:attach' | 'player:source_set' | 'player:engine_selected' | 'player:load_start' | 'player:load_ok' | 'player:load_fail' | 'player:play_request' | 'player:play_ok' | 'player:play_fail' | 'player:pause' | 'player:timeupdate' | 'player:ended' | 'player:error_nonfatal' | 'player:error_fatal' | 'player:playing' | 'qoe:first_frame' | 'qoe:rebuffer_start' | 'qoe:rebuffer_end' | 'qoe:summary' | 'player:auto_next' | 'player:playlist_index' | 'ads:break_start' | 'ads:break_end' | 'ads:ad_start' | 'ads:ad_end' | 'ssai:manifest_rewrite';
807
+ interface TelemetrySample {
808
+ ts: number;
809
+ level: TelemetryLevel;
810
+ event: TelemetryEvent;
811
+ contentId?: string;
812
+ engineId?: string;
813
+ message?: string;
814
+ data?: Record<string, unknown>;
815
+ }
816
+
817
+ /**
818
+ * High-level playback profiles.
819
+ *
820
+ * Goal: provide good defaults with *minimal configuration*.
821
+ * Users can still override per-source hints via `source.abr`.
822
+ */
823
+ interface EngineProfileHints {
824
+ /** Defaults applied only when the user did not specify `source.abr.*` */
825
+ abr?: {
826
+ maxHeight?: number;
827
+ maxBandwidth?: number;
828
+ };
829
+ /** Shaka-specific streaming knobs (best-effort, ignored by non-shaka engines). */
830
+ shakaStreaming?: {
831
+ lowLatencyMode?: boolean;
832
+ bufferingGoalSec?: number;
833
+ rebufferingGoalSec?: number;
834
+ };
835
+ }
836
+ declare function getEngineProfileHints(profile?: EngineProfileId): EngineProfileHints;
837
+ /**
838
+ * Apply profile defaults to a `PlayerSource` without overriding user-provided hints.
839
+ *
840
+ * This function is intentionally conservative: it only fills missing fields.
841
+ */
842
+ declare function applyEngineProfileToSource(source: PlayerSource, profile?: EngineProfileId): PlayerSource;
843
+
844
+ declare function createNativeEngineFactory(): EngineFactory;
845
+
846
+ declare function applyRemoteConfig(cfg: PlayerConfig, ports: Partial<PlayerPorts>): Promise<{
847
+ config: PlayerConfig;
848
+ applied: boolean;
849
+ payload?: RemoteConfigPayload;
850
+ source: 'cache' | 'network' | 'none';
851
+ }>;
852
+
853
+ export { type AdsConfig, type AdsMode, type AspectRatioMode, type CacheStore, type CapabilityReport, ConsoleLogPort, DefaultCacheStore, type DiagnosticsEngineInfo, type DiagnosticsSnapshot, type DrmConfig, type EngineEvent, type EngineFactory, type EngineProfileHints, type EngineProfileId, type EpisodeItem, type HttpPort, type LoadOptions, LocalMetadataProvider, LocalSubtitleProvider, type LogPort, type MediaTrack, MemoryStoragePort, type MetadataConfig, type MetadataHint, type MetadataMatch, type MetadataProvider, NativeEngine, type NextEpisodeHint, NoopTelemetryPort, type PlaybackCapabilities, type PlaybackEngine, type PlayerCommand, type PlayerConfig, type PlayerContext, type PlayerDiagnostics, type PlayerError, type PlayerErrorCode, type PlayerEvent, type PlayerEventBus, PlayerFacade, type PlayerPlugin, type PlayerPorts, type PlayerSnapshot, type PlayerSource, type PlayerState, type PlayerTime, type ProgressConfig, type ProgressMode, ProgressPlugin, type RemoteConfigConfig, type RemoteConfigPayload, type ResilienceConfig, ResiliencePlugin, type SeriesConfig, SimpleEventBus, type SkipSegment, type SourceType, type SsaiManifestRewriteConfig, type StoragePort, type SubtitleCandidate, type SubtitleHint, type SubtitleProvider, type SubtitleQuery, type SubtitlesConfig, type TelemetryEvent, type TelemetryLevel, type TelemetryPort, type TelemetrySample, type UiConfig, type Unsubscribe, type VideoMetadata, applyEngineProfileToSource, applyRemoteConfig, createCapabilityReport, createDefaultCapabilities, createDefaultPorts, createMediaDecodingReport, createNativeEngineFactory, createSsaiManifestRewritePlugin, detectPlaybackCapabilities, getEngineProfileHints, isCategorizedEventType, selectEngine };