boss-web-player-sdk 1.0.2

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.
@@ -0,0 +1,897 @@
1
+ import { default as default_2 } from 'react';
2
+ import { JSX } from 'react/jsx-runtime';
3
+
4
+ export declare type AnalyticsCallback = (event: AnalyticsEvent) => void;
5
+
6
+ export declare interface AnalyticsEvent {
7
+ eventType: 'play' | 'pause' | 'seek' | 'ended' | 'complete' | 'buffering_start' | 'buffering_end' | 'stall' | 'quality_change' | 'audio_track_change' | 'volume_change' | 'speed_change' | 'subtitle_change' | 'mute' | 'fast_forward' | 'fast_backward' | 'key_moments' | 'pip_toggle' | 'fullscreen_enter' | 'fullscreen_exit' | 'cast_start' | 'cast_stop' | 'error' | 'network_error' | 'session_start' | 'video_loaded' | 'first_quartile' | 'midpoint' | 'third_quartile' | 'heartbeat' | 'watch_progress' | 'streaming_time';
8
+ timestamp: number;
9
+ currentTime: number;
10
+ duration: number;
11
+ bitrate?: number;
12
+ rendition?: string;
13
+ fromRendition?: string;
14
+ volume?: number;
15
+ speed?: number;
16
+ errorMessage?: string;
17
+ errorCode?: number;
18
+ isRecoverable?: boolean;
19
+ sessionId?: string;
20
+ videoId?: string;
21
+ deviceType?: string;
22
+ networkType?: string;
23
+ watchPercentage?: number;
24
+ totalWatchTime?: number;
25
+ streamingTime?: number;
26
+ adWatchTime?: number;
27
+ bufferingCount?: number;
28
+ totalBufferingTime?: number;
29
+ seekFrom?: number;
30
+ seekTo?: number;
31
+ isMuted?: boolean;
32
+ skipSeconds?: number;
33
+ momentTitle?: string;
34
+ momentTime?: number;
35
+ pipState?: 'entered' | 'exited';
36
+ audioTrack?: string;
37
+ subtitleLanguage?: string;
38
+ subtitleEnabled?: boolean;
39
+ isAutoSwitch?: boolean;
40
+ }
41
+
42
+ declare class AnalyticsModule {
43
+ private callback?;
44
+ private heartbeatInterval?;
45
+ private watchProgressTimer?;
46
+ private isBuffering;
47
+ private sessionId;
48
+ private videoId;
49
+ private deviceType;
50
+ private networkType;
51
+ private adBreaks;
52
+ private isFirstPlay;
53
+ private watchStartTime;
54
+ private totalWatchTime;
55
+ private streamingWindowStart;
56
+ private streamingTime;
57
+ private isInAdBreak;
58
+ private adWatchWindowStart;
59
+ private adWatchTime;
60
+ private bufferingCount;
61
+ private totalBufferingTime;
62
+ private bufferingStartTime;
63
+ private milestonesHit;
64
+ private lastHeartbeatPosition;
65
+ private lastHeartbeatAt;
66
+ constructor(callback?: AnalyticsCallback, src?: string, seed?: AnalyticsSessionSnapshot, videoId?: string);
67
+ getSessionSnapshot(): AnalyticsSessionSnapshot;
68
+ registerCallback(callback: AnalyticsCallback): void;
69
+ setAdBreaks(adBreaks: Array<{
70
+ startTime: number;
71
+ endTime: number;
72
+ }>): void;
73
+ private getTotalAdDuration;
74
+ private getElapsedAdTime;
75
+ track(eventType: AnalyticsEvent['eventType'], videoEl: HTMLVideoElement, extra?: Partial<AnalyticsEvent>): void;
76
+ startHeartbeat(videoEl: HTMLVideoElement, getExtra: () => Partial<AnalyticsEvent>, watchProgressInterval?: number): void;
77
+ stopHeartbeat(): void;
78
+ setBuffering(isBuffering: boolean, videoEl: HTMLVideoElement, getExtra: () => Partial<AnalyticsEvent>): void;
79
+ setAdBreak(inAdBreak: boolean, videoEl: HTMLVideoElement): void;
80
+ private checkMilestones;
81
+ private checkStall;
82
+ private getTotalWatchTime;
83
+ private getStreamingTime;
84
+ private getAdWatchTime;
85
+ private dispatchRaw;
86
+ private generateSessionId;
87
+ private extractVideoId;
88
+ private getDeviceType;
89
+ private getNetworkType;
90
+ }
91
+
92
+ declare interface AnalyticsSessionSnapshot {
93
+ sessionId: string;
94
+ /** True once session_start has fired — a seeded module must not fire it again. */
95
+ sessionStartFired: boolean;
96
+ totalWatchTime: number;
97
+ streamingTime: number;
98
+ adWatchTime: number;
99
+ bufferingCount: number;
100
+ totalBufferingTime: number;
101
+ /** Quartile milestones already dispatched ('first_quartile' | 'midpoint' | 'third_quartile'). */
102
+ milestonesHit: string[];
103
+ }
104
+
105
+ export declare function BossVideoPlayer({ playerKey, src, subtitles, licenseKey, themeColor, firebaseConfig, proxyDomains, apiBaseUrl, adTrackingUrl, overlayAdUrl, videoId: videoIdProp, title, watermark, autoplay, muted, autoFullscreen, enableControls, enableSkipAd, skipAdAfterSeconds, chaptersDisplayMode, volumeControlType, subtitleStyle, drmConfig, onAnalyticsEvent, delegate, onKeyMomentsLoaded, initialTime, onTimeUpdate, }: BossVideoPlayerProps): JSX.Element;
106
+
107
+ export declare interface BossVideoPlayerProps {
108
+ /** Player key from the GoBOSS dashboard — identifies your tenant. */
109
+ playerKey: string;
110
+ /**
111
+ * Stream source(s) to play.
112
+ * - Pass a single URL string for standard single-stream playback.
113
+ * - Pass an array of `{ label, url }` objects to let the user switch between
114
+ * multiple streams at runtime (e.g. 4K, Dolby, Stereo). The first item plays by default.
115
+ */
116
+ src: string | SourceItem[];
117
+ /**
118
+ * External subtitle tracks (WebVTT/SRT) to load alongside any tracks
119
+ * auto-detected from the M3U8 manifest. Merged into a single subtitle menu.
120
+ */
121
+ subtitles?: SubtitleTrack[];
122
+ /** License key for playback authorization. */
123
+ licenseKey?: string;
124
+ /** Accent color for the player UI (hex). Defaults to '#6366f1'. */
125
+ themeColor?: string;
126
+ /** Firebase config for analytics. */
127
+ firebaseConfig?: FirebaseConfig;
128
+ /**
129
+ * CDN proxy domain mappings as a comma-separated string.
130
+ * Format: "cdn.example.com=/my-proxy,other.cdn.com=/other-proxy"
131
+ * Any request URL matching a domain is rewritten to the proxy path.
132
+ */
133
+ proxyDomains?: string;
134
+ /**
135
+ * Base URL for the GoBOSS API. Defaults to 'https://dev-api.go-boss.com'.
136
+ * Override this if you need to route through a reverse proxy.
137
+ */
138
+ apiBaseUrl?: string;
139
+ /** AWS MediaTailor ad tracking URL for skip-ad flow. */
140
+ adTrackingUrl?: string;
141
+ /** AWS MediaTailor overlay ad tracking URL. */
142
+ overlayAdUrl?: string;
143
+ videoId?: string;
144
+ /** Video title shown in the player UI. */
145
+ title?: string;
146
+ /** Watermark text stamped on the video. */
147
+ watermark?: string;
148
+ autoplay?: boolean;
149
+ muted?: boolean;
150
+ autoFullscreen?: boolean;
151
+ enableControls?: boolean;
152
+ enableSkipAd?: boolean;
153
+ skipAdAfterSeconds?: number;
154
+ chaptersDisplayMode?: 'strip' | 'panel';
155
+ volumeControlType?: 'slider' | 'button';
156
+ subtitleStyle?: SubtitleStyle;
157
+ drmConfig?: DRMConfig;
158
+ /** Called for every analytics event — lets consumers add custom tracking. */
159
+ onAnalyticsEvent?: (event: AnalyticsEvent) => void;
160
+ /**
161
+ * Typed per-event callbacks (playerDidStartPlaying, playerDidPause, …) that
162
+ * receive only the arguments relevant to each event. Prefer this over
163
+ * `onAnalyticsEvent` for analytics, logging, and business logic; implement
164
+ * only the methods you need.
165
+ */
166
+ delegate?: PlayerDelegate;
167
+ /** Called when chapter/key-moment data is parsed from the stream. */
168
+ onKeyMomentsLoaded?: (keyMoments: Chapter[]) => void;
169
+ /** Seek to this position (in seconds) when the video is ready. Use for resume-playback. */
170
+ initialTime?: number;
171
+ /** Called on every timeupdate with the current playback position in seconds. Use to persist watch progress. */
172
+ onTimeUpdate?: (currentTime: number) => void;
173
+ }
174
+
175
+ declare class CastingModule {
176
+ private isCasting;
177
+ private castDeviceName;
178
+ private onCastStateChangeCallback?;
179
+ private onRemoteStateChangeCallback?;
180
+ private castContext;
181
+ private remotePlayer;
182
+ private remotePlayerController;
183
+ constructor();
184
+ private initCastApi;
185
+ getCastingState(): {
186
+ isCasting: boolean;
187
+ deviceName: string;
188
+ };
189
+ getRemoteState(): RemoteCastState | null;
190
+ /**
191
+ * Derives the Cast MediaInfo content type from the stream URL so HLS/DASH manifests
192
+ * aren't sent to the receiver mislabeled as video/mp4, which causes load failures.
193
+ */
194
+ private getContentType;
195
+ /**
196
+ * Prompts the native Google Cast device picker overlay, then loads media on the remote device.
197
+ * The caller is responsible for pausing local playback before/after this resolves.
198
+ */
199
+ requestSession(currentSrc: string, currentTime: number): Promise<void>;
200
+ /**
201
+ * Disconnects the current cast session.
202
+ */
203
+ stopCasting(): void;
204
+ remotePlayPause(): void;
205
+ remoteSeek(time: number): void;
206
+ remoteSetVolume(volume: number): void;
207
+ remoteSetMute(muted: boolean): void;
208
+ onCastStateChange(callback: (isCasting: boolean, deviceName: string) => void): void;
209
+ onRemoteStateChange(callback: (state: RemoteCastState) => void): void;
210
+ requestAirPlaySession(videoElement: HTMLVideoElement): void;
211
+ stopAirPlay(videoElement: HTMLVideoElement): void;
212
+ isAirPlayAvailable(): boolean;
213
+ }
214
+
215
+ export declare interface Chapter {
216
+ title: string;
217
+ startTime: number;
218
+ endTime: number;
219
+ thumbnail?: string;
220
+ description?: string;
221
+ }
222
+
223
+ declare class ChaptersModule extends KeyMomentsModule {
224
+ }
225
+
226
+ /**
227
+ * Mounts the full video player into any DOM element.
228
+ * Identical to <BossVideoPlayer> — handles player key validation, feature gating,
229
+ * session tracking, and heartbeat automatically.
230
+ *
231
+ * Use this in Vue, Angular, Vanilla JS, or any environment where JSX is unavailable.
232
+ *
233
+ * @example Vanilla JS
234
+ * const player = createPlayer(document.getElementById('player'), {
235
+ * playerKey: 'bsp_abc123',
236
+ * src: 'https://my-cdn.com/video.m3u8',
237
+ * });
238
+ * player.destroy(); // cleanup
239
+ *
240
+ * @example Vue 3
241
+ * const player = createPlayer(containerRef.value, { playerKey: 'bsp_abc123', src: props.src });
242
+ * onUnmounted(() => player.destroy());
243
+ *
244
+ * @example Angular
245
+ * const player = createPlayer(this.containerRef.nativeElement, { playerKey: 'bsp_abc123', src: this.src });
246
+ * ngOnDestroy() { this.player.destroy(); }
247
+ */
248
+ export declare function createPlayer(container: HTMLElement, props: PlayerProps): PlayerInstance;
249
+
250
+ export declare interface DRMConfig {
251
+ widevineLicenseUrl?: string;
252
+ playreadyLicenseUrl?: string;
253
+ fairplayLicenseUrl?: string;
254
+ authToken?: string;
255
+ authHeader?: string;
256
+ allowedDomains?: string[];
257
+ watermarkText?: string;
258
+ }
259
+
260
+ declare class DRMModule {
261
+ private config?;
262
+ private watermarkElement?;
263
+ private floatInterval?;
264
+ constructor(config?: DRMConfig);
265
+ /**
266
+ * Validates if the active origin domain is permitted to load this stream asset.
267
+ */
268
+ validateDomain(currentDomain: string): boolean;
269
+ getAuthToken(): string | undefined;
270
+ getAuthHeader(): string | undefined;
271
+ getRequestHeaders(): Record<string, string>;
272
+ /**
273
+ * Injects an animated floating watermark on top of the media container to obstruct piracy or recording.
274
+ */
275
+ setupWatermark(container: HTMLElement): void;
276
+ /**
277
+ * Cleans up active watermarks.
278
+ */
279
+ removeWatermark(): void;
280
+ isDRMProtected(): boolean;
281
+ getDRMDetails(): {
282
+ widevine: string | null;
283
+ playready: string | null;
284
+ fairplay: string | null;
285
+ authTokenPresent: boolean;
286
+ allowedDomains: string[];
287
+ };
288
+ }
289
+
290
+ /**
291
+ * Ends the session and finalizes billed minutes. `currentTime` and `duration`
292
+ * are required by the API — pass the final playback position and content length.
293
+ */
294
+ export declare function endSession(playerKey: string, sessionId: string, currentTime?: number, duration?: number, apiBase?: string): Promise<void>;
295
+
296
+ export declare type FeatureFlags = {
297
+ m3u8_playback: boolean;
298
+ subtitle_support: boolean;
299
+ playback_speed: boolean;
300
+ airplay: boolean;
301
+ chromecast: boolean;
302
+ picture_in_picture: boolean;
303
+ multiple_resolution: boolean;
304
+ multiple_audio: boolean;
305
+ key_moments: boolean;
306
+ drm_protection: boolean;
307
+ screen_recording_prevention: boolean;
308
+ signal_strength: boolean;
309
+ ad_support: boolean;
310
+ };
311
+
312
+ /**
313
+ * Sends all player analytics events to Firebase Analytics (GA4).
314
+ *
315
+ * Events appear in:
316
+ * Firebase Console → Analytics → Events (24-48 hr aggregated view)
317
+ * Firebase Console → Analytics → DebugView (real-time while testing)
318
+ * Google Analytics 4 → Reports → Realtime (within a few minutes)
319
+ *
320
+ * No Firestore / database setup required — Firebase Analytics handles all storage.
321
+ */
322
+ export declare class FirebaseAnalyticsProvider {
323
+ private analytics;
324
+ private logEventFn;
325
+ private pendingEvents;
326
+ constructor(config: FirebaseConfig);
327
+ private init;
328
+ private flushPending;
329
+ createCallback(): AnalyticsCallback;
330
+ private handleEvent;
331
+ private logToFirebase;
332
+ private buildParams;
333
+ destroy(): Promise<void>;
334
+ }
335
+
336
+ export declare interface FirebaseConfig {
337
+ apiKey: string;
338
+ authDomain: string;
339
+ projectId: string;
340
+ storageBucket: string;
341
+ messagingSenderId: string;
342
+ appId: string;
343
+ measurementId?: string;
344
+ }
345
+
346
+ export declare function initSdk(playerKey: string, apiBase?: string, options?: SdkInitOptions): Promise<SdkInitResponse>;
347
+
348
+ export declare type KeyMoment = Chapter;
349
+
350
+ declare class KeyMomentsModule {
351
+ private keyMoments;
352
+ private currentMomentIndex;
353
+ private onMomentChangeCallback?;
354
+ constructor(keyMoments?: KeyMoment[]);
355
+ /**
356
+ * Sorts and stores the key moments.
357
+ */
358
+ setKeyMoments(keyMoments: KeyMoment[]): void;
359
+ getKeyMoments(): KeyMoment[];
360
+ /**
361
+ * Locates the key moment corresponding to the active playback timestamp.
362
+ */
363
+ getCurrentMoment(currentTime: number): {
364
+ keyMoment: KeyMoment | null;
365
+ index: number;
366
+ };
367
+ /**
368
+ * Evaluates if playback has crossed a key moment boundary.
369
+ */
370
+ updateTime(currentTime: number): void;
371
+ /**
372
+ * Subscribes a callback to change events.
373
+ */
374
+ onMomentChange(callback: (keyMoment: KeyMoment | null, index: number) => void): void;
375
+ setChapters(chapters: KeyMoment[]): void;
376
+ getChapters(): KeyMoment[];
377
+ getCurrentChapter(currentTime: number): {
378
+ chapter: KeyMoment | null;
379
+ index: number;
380
+ };
381
+ onChapterChange(callback: (chapter: KeyMoment | null, index: number) => void): void;
382
+ }
383
+
384
+ /**
385
+ * Typed per-event callbacks for tenant applications.
386
+ *
387
+ * Unlike `AnalyticsCallback` (which receives the full analytics event for every
388
+ * event type), each delegate method receives only the arguments relevant to that
389
+ * event — clean signatures for logging, custom UI, and business logic.
390
+ *
391
+ * All methods are optional; implement only the ones you need.
392
+ *
393
+ * Conventions:
394
+ * - Arguments named `*Time` / `*At` are playback positions in seconds.
395
+ * - `watchDuration` and `seekDuration` are wall-clock seconds.
396
+ * - `from*` arguments are `undefined` when there was no previous value
397
+ * (e.g. the first quality selection after load).
398
+ */
399
+ export declare interface PlayerDelegate {
400
+ /** Playback started for the first time (or restarted after finishing). */
401
+ playerDidStartPlaying?(startTime: number, autoplay: boolean): void;
402
+ /** Playback resumed after a pause. */
403
+ playerDidResumePlaying?(resumedAt: number): void;
404
+ /** Playback paused by the user (not fired for the implicit pause at end of stream). */
405
+ playerDidPause?(pausedAt: number): void;
406
+ /** Playback reached the end of the stream. */
407
+ playerDidFinishPlaying?(finishedAt: number, watchDuration: number): void;
408
+ /** The player was torn down (unmount / tab close) before playback finished. Fired at most once. */
409
+ playerDidDismiss?(dismissedAt: number, watchDuration: number): void;
410
+ /** A seek started (scrubber, ±10s skip). Fired once per seek gesture. */
411
+ playerDidStartSeeking?(fromTime: number): void;
412
+ /** The seek landed. `fromTime` is where the gesture began, `seekDuration` is wall-clock seconds it took. */
413
+ playerDidFinishSeeking?(fromTime: number, toTime: number, seekDuration: number): void;
414
+ /** Playback position update — fires on every native `timeupdate` (roughly 4×/second). */
415
+ playerDidUpdateCurrentTime?(currentTime: number, duration: number): void;
416
+ playerDidStartBuffering?(startedAt: number): void;
417
+ playerDidFinishBuffering?(endedAt: number): void;
418
+ /** Rendition changed. `isAutoSwitch` is true when ABR picked the level, false for a manual selection. */
419
+ playerDidChangeQuality?(fromQuality: string | undefined, toQuality: string, isAutoSwitch: boolean): void;
420
+ /** Subtitle track changed (languages, e.g. 'en' → 'fr'). */
421
+ playerDidChangeSubtitle?(fromSubtitle: string | undefined, toSubtitle: string): void;
422
+ /** Subtitles were turned off. `disabledSubtitle` is the language that was active. */
423
+ playerDidDisableSubtitle?(disabledSubtitle: string | undefined): void;
424
+ /** Audio track changed (track labels). */
425
+ playerDidChangeAudioTrack?(fromAudioTrack: string | undefined, toAudioTrack: string): void;
426
+ playerDidChangePlaybackSpeed?(fromSpeed: number, toSpeed: number): void;
427
+ /** `orientation` is the current `screen.orientation.type` (e.g. 'landscape-primary') when available. */
428
+ playerDidEnterFullscreen?(orientation?: string): void;
429
+ playerDidExitFullscreen?(orientation?: string): void;
430
+ /** A key moment was selected. `fromTime`/`toTime` are the chapter's start/end. */
431
+ playerDidSelectKeyMoment?(chapterId: string | undefined, chapterTitle: string, fromTime: number, toTime: number): void;
432
+ playerDidEnterPictureInPicture?(enteredAt?: number): void;
433
+ playerDidExitPictureInPicture?(exitedAt?: number): void;
434
+ /** A media/playback error. `isRecoverable` is true when the player will attempt recovery itself. */
435
+ playerDidEncounterPlaybackError?(errorCode: string, message: string, isRecoverable: boolean): void;
436
+ /** A network/CDN error. `statusCode` is the HTTP status when known, 0 otherwise. */
437
+ playerDidEncounterNetworkError?(statusCode: number, message: string, isRecoverable: boolean): void;
438
+ }
439
+
440
+ /**
441
+ * In-session telemetry payload for POST /api/player/sdk/events.
442
+ * `eventType`, `sessionId` and `currentTime` are always required; `videoId`,
443
+ * `videoTitle` and any event-specific extras (e.g. `from`/`to`, `speed`,
444
+ * `bufferingCount`) are sent at the top level — never wrapped in a client
445
+ * `properties` object (the server nests them).
446
+ */
447
+ export declare interface PlayerEventPayload {
448
+ eventType: string;
449
+ sessionId: string;
450
+ currentTime: number;
451
+ videoId?: string;
452
+ videoTitle?: string;
453
+ [key: string]: unknown;
454
+ }
455
+
456
+ export declare interface PlayerInstance {
457
+ /** Re-render the player with updated props (src change, theme, etc.) */
458
+ update(props: Partial<PlayerProps>): void;
459
+ /** Unmounts the player and releases all resources */
460
+ destroy(): void;
461
+ }
462
+
463
+ export declare interface PlayerProps extends BossVideoPlayerProps {
464
+ }
465
+
466
+ export declare interface PlayerSDKOptions {
467
+ container: HTMLElement;
468
+ src: string;
469
+ autoplay?: boolean;
470
+ muted?: boolean;
471
+ controls?: boolean;
472
+ chapters?: Chapter[];
473
+ keyMoments?: KeyMoment[];
474
+ subtitles?: SubtitleTrack[];
475
+ thumbnails?: ThumbnailConfig;
476
+ drm?: DRMConfig;
477
+ analyticsCallback?: AnalyticsCallback;
478
+ videoId?: string;
479
+ analyticsSessionSeed?: AnalyticsSessionSnapshot;
480
+ watchProgressInterval?: number;
481
+ themeColor?: string;
482
+ watermark?: string;
483
+ /**
484
+ * Optional URL transformer applied to every HLS segment/manifest request.
485
+ * Use this to route requests through a CORS proxy without hardcoding domains.
486
+ * Example: (url) => url.replace('https://cdn.example.com', '/my-proxy')
487
+ */
488
+ urlTransformer?: (url: string) => string;
489
+ }
490
+
491
+ declare interface RemoteCastState {
492
+ currentTime: number;
493
+ duration: number;
494
+ isPaused: boolean;
495
+ volumeLevel: number;
496
+ isMuted: boolean;
497
+ }
498
+
499
+ export declare type SdkInitData = {
500
+ tenantId: string;
501
+ features: SdkInitFeatures;
502
+ planTier: string;
503
+ /** How often to call /session/heartbeat. Falls back to 30s when absent. */
504
+ heartbeatIntervalSeconds?: number;
505
+ };
506
+
507
+ export declare type SdkInitFeatures = {
508
+ subtitles: boolean;
509
+ ads: boolean;
510
+ chapterMarkers: boolean;
511
+ [key: string]: boolean;
512
+ };
513
+
514
+ /** Optional context accepted by POST /init. `referrer` is intentionally not sent. */
515
+ export declare interface SdkInitOptions {
516
+ videoUrl?: string;
517
+ }
518
+
519
+ export declare type SdkInitResponse = {
520
+ result: {
521
+ data: SdkInitData;
522
+ metadata: Record<string, unknown>;
523
+ };
524
+ };
525
+
526
+ export declare type SdkPackage = {
527
+ package: string;
528
+ is_active: boolean;
529
+ features: FeatureFlags;
530
+ limits: {
531
+ streaming_minutes: number;
532
+ };
533
+ };
534
+
535
+ /**
536
+ * Records a single in-session analytics event. One event per request.
537
+ * The endpoint returns 204 No Content on success, so there is no body to parse.
538
+ */
539
+ export declare function sendEvent(playerKey: string, payload: PlayerEventPayload, apiBase?: string): Promise<void>;
540
+
541
+ export declare function sendHeartbeat(playerKey: string, sessionId: string, currentTime: number, duration: number, apiBase?: string): Promise<void>;
542
+
543
+ export declare interface SourceItem {
544
+ label: string;
545
+ url: string;
546
+ }
547
+
548
+ export declare function startSession(playerKey: string, options: StartSessionOptions, apiBase?: string): Promise<void>;
549
+
550
+ /**
551
+ * Body for POST /session/start. Only `sessionId` is required for HTTP acceptance,
552
+ * but `videoId`, `videoTitle` and `contentDurationSec` drive the video-level CMS
553
+ * analytics (Top Videos, retention, completion) — send them whenever they're known.
554
+ * `platform` and `referrer` are intentionally not sent by this SDK.
555
+ */
556
+ export declare interface StartSessionOptions {
557
+ /** Client-generated UUID identifying this play. */
558
+ sessionId: string;
559
+ /** Stable content ID — the grouping key for video-level analytics. */
560
+ videoId?: string;
561
+ /** Display title for CMS dashboards. */
562
+ videoTitle?: string;
563
+ /** Playback URL. */
564
+ videoUrl?: string;
565
+ /** Full content length in seconds, when known. */
566
+ contentDurationSec?: number;
567
+ }
568
+
569
+ declare class SubtitlesModule {
570
+ private tracks;
571
+ private activeTrackId;
572
+ private currentCues;
573
+ private activeText;
574
+ private onSubtitleChangeCallback?;
575
+ private onTrackSwitchCallback?;
576
+ constructor(tracks?: SubtitleTrack[]);
577
+ getTracks(): SubtitleTrack[];
578
+ getActiveTrackId(): string;
579
+ setHlsTracks(hlsTracks: SubtitleTrack[]): void;
580
+ addTrack(track: SubtitleTrack): void;
581
+ onTrackSwitch(callback: (track: SubtitleTrack | null) => void): void;
582
+ setCustomSubtitleText(text: string): void;
583
+ /**
584
+ * Switches the active track. Fetches external tracks if they need loading.
585
+ */
586
+ setActiveTrack(trackId: string): Promise<void>;
587
+ /**
588
+ * Scans the subtitle cues for the current playback position.
589
+ */
590
+ updateTime(currentTime: number): void;
591
+ /**
592
+ * Triggers the text update callback if a change in caption is detected.
593
+ */
594
+ private updateActiveText;
595
+ onSubtitleChange(callback: (text: string) => void): void;
596
+ /**
597
+ * Robust parser supporting WebVTT and SRT formats.
598
+ */
599
+ private parseVttOrSrt;
600
+ }
601
+
602
+ export declare interface SubtitleStyle {
603
+ size?: 'small' | 'medium' | 'large';
604
+ background?: 'none' | 'dim' | 'solid';
605
+ color?: string;
606
+ }
607
+
608
+ declare interface SubtitleStyle_2 {
609
+ size?: 'small' | 'medium' | 'large';
610
+ background?: 'none' | 'dim' | 'solid';
611
+ color?: string;
612
+ }
613
+
614
+ export declare interface SubtitleTrack {
615
+ id: string;
616
+ label: string;
617
+ lang: string;
618
+ src?: string;
619
+ isDefault?: boolean;
620
+ cues?: Array<{
621
+ startTime: number;
622
+ endTime: number;
623
+ text: string;
624
+ }>;
625
+ isHls?: boolean;
626
+ hlsTrackId?: number;
627
+ isNative?: boolean;
628
+ nativeTrackIndex?: number;
629
+ }
630
+
631
+ export declare interface ThumbnailConfig {
632
+ spriteUrl: string;
633
+ tileWidth: number;
634
+ tileHeight: number;
635
+ columns: number;
636
+ rows: number;
637
+ interval: number;
638
+ totalCount: number;
639
+ }
640
+
641
+ declare class ThumbnailsModule {
642
+ private config?;
643
+ constructor(config?: ThumbnailConfig);
644
+ /**
645
+ * Calculates the position offset inside the sprite sheet for a given seek hover time.
646
+ */
647
+ getThumbnail(time: number): {
648
+ url: string;
649
+ x: number;
650
+ y: number;
651
+ width: number;
652
+ height: number;
653
+ } | null;
654
+ hasThumbnails(): boolean;
655
+ }
656
+
657
+ export declare class VideoPlayerSDK {
658
+ private options;
659
+ private videoElement;
660
+ private hlsInstance;
661
+ private dashInstance;
662
+ private activeQuality;
663
+ private qualityLevels;
664
+ analytics: AnalyticsModule;
665
+ keyMoments: KeyMomentsModule;
666
+ chapters: ChaptersModule;
667
+ subtitles: SubtitlesModule;
668
+ drm: DRMModule;
669
+ casting: CastingModule;
670
+ thumbnails: ThumbnailsModule;
671
+ private hlsSubtitleCues;
672
+ private hlsActiveSubtitleKey;
673
+ private audioTrackList;
674
+ private adBreaks;
675
+ private isAdBreak;
676
+ private adBreaksFromAvails;
677
+ private contentPlaybackRate;
678
+ private networkErrorRetries;
679
+ private mediaErrorRecovered;
680
+ private static readonly MAX_NETWORK_RETRIES;
681
+ private static readonly TERMINAL_HTTP_STATUSES;
682
+ private static readonly STALL_TIMEOUT_MS;
683
+ private stallTimer;
684
+ private isBuffering;
685
+ private progressTick;
686
+ private hasFailed;
687
+ private lastPlaybackPos;
688
+ private pendingSeekAttribution;
689
+ private eventListeners;
690
+ constructor(options: PlayerSDKOptions);
691
+ /**
692
+ * Builds the video DOM hierarchy, binds events, validates domain restrictions, and mounts DRM watermarks.
693
+ */
694
+ private initializePlayer;
695
+ /**
696
+ * Binds direct event listeners to the native HTML5 Video tag, routing them to our sub-packages.
697
+ */
698
+ private bindMediaEvents;
699
+ private rewriteToProxy;
700
+ /**
701
+ * Initializes HLS adaptive streaming through Hls.js or falls back to native browser playback.
702
+ */
703
+ private loadSource;
704
+ /**
705
+ * Sets up global lock-screen metadata and media control keys (Media Session API).
706
+ */
707
+ private setupBackgroundControls;
708
+ play(): void;
709
+ pause(): void;
710
+ seek(seconds: number): void;
711
+ /**
712
+ * Attributes the NEXT seek to a specific user gesture so telemetry reports it as
713
+ * `fast_forward` / `fast_backward` / `key_moments` instead of a generic `seek`.
714
+ * Call immediately before triggering the seek. The attribution expires after a
715
+ * short window so it can never leak onto an unrelated later seek.
716
+ */
717
+ attributeNextSeek(eventType: 'fast_forward' | 'fast_backward' | 'key_moments', extras?: Partial<AnalyticsEvent>): void;
718
+ setVolume(volume: number): void;
719
+ setMute(muted: boolean): void;
720
+ setPlaybackRate(rate: number): void;
721
+ /**
722
+ * Sets manual video quality rendition or switches back to auto bitrate scaling.
723
+ */
724
+ setQuality(qualityId: number): void;
725
+ /**
726
+ * Activates native Picture-in-Picture floating viewport.
727
+ */
728
+ togglePictureInPicture(): Promise<boolean>;
729
+ /**
730
+ * Requests element-level fullscreen.
731
+ */
732
+ toggleFullscreen(): void;
733
+ getVideoElement(): HTMLVideoElement;
734
+ getCurrentTime(): number;
735
+ getDuration(): number;
736
+ getVolume(): number;
737
+ isMuted(): boolean;
738
+ getPlaybackRate(): number;
739
+ getActiveQuality(): VideoQuality;
740
+ getQualityLevels(): VideoQuality[];
741
+ getBufferLength(): number;
742
+ getBufferedRanges(): Array<{
743
+ start: number;
744
+ end: number;
745
+ }>;
746
+ /**
747
+ * Returns true if the browser can decode at least one of the manifest's renditions.
748
+ *
749
+ * Deliberately conservative to avoid ever blocking a valid stream:
750
+ * - A codec that isn't declared is treated as playable (we never assume unsupported).
751
+ * - Any failure to verify (no MediaSource, isTypeSupported throws) returns true.
752
+ * - The stream is "playable" if even one rendition's declared codecs are all supported,
753
+ * matching how HLS.js itself selects a level.
754
+ * Returns false only when the browser explicitly reports every declared codec as
755
+ * undecodable (e.g. Dolby EC-3/AC-3 in Chrome).
756
+ */
757
+ private hasPlayableLevel;
758
+ private getAnalyticsPayload;
759
+ on(event: string, callback: (...args: any[]) => void): void;
760
+ off(event: string, callback: (...args: any[]) => void): void;
761
+ private emit;
762
+ private extractSubtitlesFromManifest;
763
+ private extractKeyMomentsFromManifest;
764
+ private extractAdBreaksFromManifest;
765
+ private buildManifestUrl;
766
+ private findFirstVariantUri;
767
+ private resolveUrl;
768
+ private static readonly AD_HOST_PATTERNS;
769
+ private static readonly AD_STITCHER_PATH_PATTERNS;
770
+ private static readonly AD_PATH_HINT_PATTERNS;
771
+ private parseSegmentUrl;
772
+ private isKnownAdSegment;
773
+ private hasAdPathHint;
774
+ private classifyAdGroups;
775
+ private mergeAdjacentRanges;
776
+ private parseAdBreaksFromFragments;
777
+ private parseAdBreaksFromPlaylist;
778
+ private sameAdBreaks;
779
+ private isTimeInAdBreak;
780
+ /**
781
+ * Syncs isAdBreak/playbackRate/ad-mode UI state against `time` and emits
782
+ * adbreakchange on transitions. Called from timeupdate, but also called
783
+ * eagerly as soon as ad breaks or metadata become known — timeupdate alone
784
+ * doesn't fire until playback actually starts, which left a preroll ad's
785
+ * first frame briefly showing content controls (rewind/forward, etc.)
786
+ * while autoplay was blocked or the source was still buffering.
787
+ */
788
+ private refreshAdBreakState;
789
+ /**
790
+ * Converts raw (stitched) video time to content-only time by subtracting the
791
+ * ad time elapsed before `time`. During an ad break the result is frozen at
792
+ * the content timestamp where the break started. Identity when no ad breaks
793
+ * are known. Mirrors the seekbar mapping in the UI layer.
794
+ */
795
+ private videoTimeToContentTime;
796
+ getAdBreaks(): Array<{
797
+ startTime: number;
798
+ endTime: number;
799
+ }>;
800
+ setAdBreaks(breaks: Array<{
801
+ startTime: number;
802
+ endTime: number;
803
+ }>): void;
804
+ isInAdBreak(): boolean;
805
+ getAudioTracks(): Array<{
806
+ id: number;
807
+ label: string;
808
+ lang: string;
809
+ }>;
810
+ setAudioTrack(id: number): void;
811
+ /**
812
+ * Arms a one-shot timer while buffering. If the timer fires and no data has
813
+ * arrived since it was armed (progressTick unchanged) and we're still
814
+ * buffering, the stream is considered dead. If data did arrive but we're still
815
+ * buffering (slow connection), it re-arms for another window rather than
816
+ * failing — so slow internet never trips it.
817
+ */
818
+ private armStallWatchdog;
819
+ private clearStallWatchdog;
820
+ /**
821
+ * Terminal error path: emit 'error' (which drives the error screen) and tear
822
+ * down the player. Guarded so it runs at most once, no matter how many error
823
+ * events fire during teardown.
824
+ */
825
+ private fail;
826
+ destroy(): void;
827
+ }
828
+
829
+ export declare interface VideoQuality {
830
+ id: number;
831
+ height: number;
832
+ width: number;
833
+ bitrate: number;
834
+ label: string;
835
+ }
836
+
837
+ export declare const WebVideoPlayer: default_2.FC<WebVideoPlayerProps>;
838
+
839
+ declare interface WebVideoPlayerProps {
840
+ src: string;
841
+ autoplay?: boolean;
842
+ muted?: boolean;
843
+ chapters?: Chapter[];
844
+ keyMoments?: KeyMoment[];
845
+ subtitles?: SubtitleTrack[];
846
+ watermark?: string;
847
+ themeColor?: string;
848
+ drmConfig?: any;
849
+ firebaseConfig?: FirebaseConfig;
850
+ onAnalyticsEvent?: (event: AnalyticsEvent) => void;
851
+ onAdBreakChange?: (active: boolean) => void;
852
+ onKeyMomentsLoaded?: (keyMoments: KeyMoment[]) => void;
853
+ enableControls?: boolean;
854
+ enableChromecast?: boolean;
855
+ enableKeyMoments?: boolean;
856
+ enableSubtitles?: boolean;
857
+ enableQuality?: boolean;
858
+ enablePlaybackSpeed?: boolean;
859
+ enablePiP?: boolean;
860
+ enableFullscreen?: boolean;
861
+ autoFullscreen?: boolean;
862
+ watchProgressInterval?: number;
863
+ subtitleStyle?: SubtitleStyle_2;
864
+ /**
865
+ * Pre-resolved entitlement package from BossVideoPlayer.
866
+ * When provided, features are determined exclusively by this package (API mode).
867
+ * When absent, features fall back to explicit props with true defaults (standalone mode).
868
+ */
869
+ resolvedEntitlement?: {
870
+ package: string;
871
+ is_active: boolean;
872
+ features: Record<string, boolean>;
873
+ limits: {
874
+ streaming_minutes: number;
875
+ };
876
+ } | null;
877
+ licenseKey?: string | null;
878
+ title?: string;
879
+ chaptersDisplayMode?: 'strip' | 'panel';
880
+ volumeControlType?: 'slider' | 'button';
881
+ urlTransformer?: (url: string) => string;
882
+ pauseAdOnTabSwitch?: boolean;
883
+ enableSkipAd?: boolean;
884
+ skipAdAfterSeconds?: number;
885
+ overlayAdUrl?: string;
886
+ adTrackingUrl?: string;
887
+ videoId?: string;
888
+ sources?: SourceItem[];
889
+ activeSourceIndex?: number;
890
+ onSourceChange?: (index: number) => void;
891
+ /** Seek to this position (in seconds) as soon as the video is ready. Use for resume-playback. */
892
+ initialTime?: number;
893
+ /** Called on every timeupdate with the current playback position in seconds. Use to persist watch progress. */
894
+ onTimeUpdate?: (currentTime: number) => void;
895
+ }
896
+
897
+ export { }