stormcloud-video-player 0.7.50 → 0.8.1

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 (48) hide show
  1. package/README.md +269 -228
  2. package/dist/stormcloud-vp.min.js +1 -1
  3. package/lib/index.cjs +5197 -6747
  4. package/lib/index.cjs.map +1 -1
  5. package/lib/index.d.cts +196 -195
  6. package/lib/index.d.ts +196 -195
  7. package/lib/index.js +5321 -6863
  8. package/lib/index.js.map +1 -1
  9. package/lib/player/StormcloudVideoPlayer.cjs +4802 -3170
  10. package/lib/player/StormcloudVideoPlayer.cjs.map +1 -1
  11. package/lib/player/StormcloudVideoPlayer.d.cts +113 -70
  12. package/lib/players/HlsPlayer.cjs +4810 -3179
  13. package/lib/players/HlsPlayer.cjs.map +1 -1
  14. package/lib/players/HlsPlayer.d.cts +1 -1
  15. package/lib/players/index.cjs +4877 -3246
  16. package/lib/players/index.cjs.map +1 -1
  17. package/lib/sdk/hlsAdPlayer.cjs +1021 -0
  18. package/lib/sdk/hlsAdPlayer.cjs.map +1 -0
  19. package/lib/sdk/hlsAdPlayer.d.cts +10 -0
  20. package/lib/sdk/pal.cjs +358 -0
  21. package/lib/sdk/pal.cjs.map +1 -0
  22. package/lib/sdk/pal.d.cts +28 -0
  23. package/lib/{types-CIHDHY7A.d.cts → types-DDwAfBLt.d.cts} +11 -10
  24. package/lib/ui/StormcloudVideoPlayer.cjs +5070 -6609
  25. package/lib/ui/StormcloudVideoPlayer.cjs.map +1 -1
  26. package/lib/ui/StormcloudVideoPlayer.d.cts +1 -14
  27. package/lib/utils/browserCompat.cjs +10 -114
  28. package/lib/utils/browserCompat.cjs.map +1 -1
  29. package/lib/utils/browserCompat.d.cts +3 -7
  30. package/lib/utils/tracking.cjs +179 -150
  31. package/lib/utils/tracking.cjs.map +1 -1
  32. package/lib/utils/tracking.d.cts +11 -6
  33. package/package.json +1 -2
  34. package/lib/sdk/adstormPlayer.cjs +0 -1567
  35. package/lib/sdk/adstormPlayer.cjs.map +0 -1
  36. package/lib/sdk/adstormPlayer.d.cts +0 -23
  37. package/lib/sdk/vastParser.cjs +0 -517
  38. package/lib/sdk/vastParser.cjs.map +0 -1
  39. package/lib/sdk/vastParser.d.cts +0 -45
  40. package/lib/ui/OverlayRenderer.cjs +0 -2376
  41. package/lib/ui/OverlayRenderer.cjs.map +0 -1
  42. package/lib/ui/OverlayRenderer.d.cts +0 -17
  43. package/lib/utils/adcision.cjs +0 -282
  44. package/lib/utils/adcision.cjs.map +0 -1
  45. package/lib/utils/adcision.d.cts +0 -31
  46. package/lib/utils/overlays.cjs +0 -354
  47. package/lib/utils/overlays.cjs.map +0 -1
  48. package/lib/utils/overlays.d.cts +0 -70
package/README.md CHANGED
@@ -1,19 +1,20 @@
1
1
  # Stormcloud Video Player
2
2
 
3
- A professional video player with advanced ad integration for web applications. Built with precision ad break alignment, SCTE-35 signal parsing, and Prebid Server integration for server-side header bidding and VAST ad playback. Now featuring a modern, extensible architecture inspired by react-player.
3
+ A professional video player with advanced ad integration for web applications. Built with precision ad break alignment, SCTE-35 signal parsing, VMAP ad-break scheduling, custom VAST ad serving, and optional Google IMA SDK integration for seamless ad playback. Now featuring a modern, extensible architecture inspired by react-player.
4
4
 
5
5
  ## 🎯 Key Features
6
6
 
7
7
  - **Multi-Format Support**: Automatic detection and playback of HLS streams and regular video files
8
8
  - **Precision Ad Alignment**: Tight synchronization with SCTE-35 CUE-OUT signals
9
+ - **VMAP 1.0 Support**: Schedule pre-roll, mid-roll, and post-roll ad breaks via a VMAP manifest
9
10
  - **Smart Mid-Roll Handling**: Automatic detection and playback of remaining ad portions when joining late
10
- - **Flexible Ad Scheduling**: Support for both SCTE-35 markers and external ad schedules
11
+ - **Flexible Ad Scheduling**: Support for SCTE-35 markers, VMAP manifests, and external ad schedules
11
12
  - **Enhanced UI Controls**: Beautiful, adaptive video controls that work on any background color
12
13
  - **Live Mode Support**: Specialized controls for live streaming with volume adjustment
13
- - **Cross-Platform**: Works on desktop, mobile, tablets, and smart TVs
14
+ - **Cross-Platform & Smart TV Ready**: Desktop, mobile, tablets, LG WebOS, Samsung Tizen, Sony BRAVIA, Android TV, Roku, Apple TV
15
+ - **Automatic Browser Compatibility**: Built-in browser detection, polyfills, and automatic ad-player selection for legacy Smart TVs
14
16
  - **React Ready**: Multiple React components for different use cases
15
17
  - **TypeScript Support**: Full type definitions included
16
- - **Prebid Server Support**: Server-side header bidding via OpenRTB 2.5 with zero client-side SDK
17
18
  - **Professional Architecture**: Modular player system with lazy loading
18
19
 
19
20
  ## 🚀 Quick Start
@@ -40,9 +41,11 @@ function MyVideoApp() {
40
41
  muted={true}
41
42
  controls={true}
42
43
  showCustomControls={true} // Enable enhanced UI controls
44
+ hideLoadingIndicator={false} // Hide the built-in loading spinner
43
45
  allowNativeHls={true} // Allow native HLS for better performance
44
46
  licenseKey="your_license_key_here"
45
47
  vastMode="adstorm" // Use AdStorm mode with HLS ad player
48
+ vmapUrl="https://your-cdn.com/ads.vmap" // Optional VMAP manifest for scheduled breaks
46
49
  style={{ width: "100%", aspectRatio: "16/9" }}
47
50
  wrapperStyle={{ borderRadius: "12px", overflow: "hidden" }}
48
51
  onReady={(player) => {
@@ -78,8 +81,10 @@ function MyVideoApp() {
78
81
  // Stormcloud-specific props
79
82
  allowNativeHls={true}
80
83
  showCustomControls={true}
84
+ hideLoadingIndicator={false}
81
85
  licenseKey="your_license_key_here"
82
86
  vastMode="adstorm" // Use AdStorm mode (or omit for default mode)
87
+ vmapUrl="https://your-cdn.com/ads.vmap" // Optional: VMAP manifest URL
83
88
  onReady={(player) => {
84
89
  console.log("Player is ready!", player);
85
90
  }}
@@ -115,9 +120,12 @@ const player = new StormcloudVideoPlayer({
115
120
  muted: true,
116
121
  allowNativeHls: true, // Enable native HLS when supported
117
122
  showCustomControls: true, // Enable enhanced UI controls
123
+ hideLoadingIndicator: false, // Hide built-in loading spinner
118
124
  lowLatencyMode: false, // Set to true for live streams
119
125
  driftToleranceMs: 3000, // Drift tolerance for live streams
120
126
  licenseKey: "your_license_key_here",
127
+ vastMode: "default", // "adstorm" | "default"
128
+ vmapUrl: "https://your-cdn.com/ads.vmap", // Optional VMAP manifest for scheduled ad breaks
121
129
  onVolumeToggle: () => console.log("Volume toggled"),
122
130
  onFullscreenToggle: () => console.log("Fullscreen toggled"),
123
131
  });
@@ -127,8 +135,6 @@ await player.load();
127
135
 
128
136
  ### CDN Usage
129
137
 
130
- Include the single script tag and access everything from `window.StormcloudVP`:
131
-
132
138
  ```html
133
139
  <script src="https://cdn.jsdelivr.net/npm/stormcloud-video-player/dist/stormcloud-vp.min.js"></script>
134
140
 
@@ -147,8 +153,6 @@ Include the single script tag and access everything from `window.StormcloudVP`:
147
153
  </script>
148
154
  ```
149
155
 
150
- Prebid utilities (`createPrebidManager`, `createPrebidController`) are available on the `window.StormcloudVP` global. See the [Prebid Mode](#3-prebid-mode-server-side-header-bidding) section for a full CDN example with Prebid Server.
151
-
152
156
  ## 🏗️ Professional Architecture
153
157
 
154
158
  The Stormcloud Video Player now follows a professional, modular architecture similar to react-player:
@@ -170,7 +174,7 @@ StormcloudPlayer (Main Component)
170
174
  - **Purpose**: Handles HLS (.m3u8) streams with advanced features
171
175
  - **Features**:
172
176
  - SCTE-35 ad marker detection and processing
173
- - Prebid Server integration for VAST ad playback
177
+ - Google IMA SDK integration for VAST/VPAID ads
174
178
  - Live stream support with low-latency mode
175
179
  - Drift correction for live timing
176
180
  - Manifest-based and ID3 ad markers
@@ -269,14 +273,16 @@ interface StormcloudPlayerProps {
269
273
  immediateManifestAds?: boolean;
270
274
  debugAdTiming?: boolean;
271
275
  showCustomControls?: boolean;
276
+ hideLoadingIndicator?: boolean; // Hide the built-in loading spinner (default: false)
272
277
  licenseKey?: string;
273
278
  adFailsafeTimeoutMs?: number;
274
279
  minSegmentsBeforePlay?: number; // Number of segments to buffer before starting playback (default: 2)
275
280
 
276
281
  // Ad player configuration
277
- vastMode?: 'adstorm' | 'default'; // VAST mode (default: 'default')
278
- vastTagUrl?: string; // Custom VAST URL (if provided)
279
- prebid?: PrebidConfig; // Prebid Server configuration for ad auctions
282
+ vastMode?: 'adstorm' | 'default'; // VAST mode: 'adstorm' (HLS player + AdStorm VAST endpoint) or 'default' (IMA SDK + /ads/web endpoint) (default: 'default')
283
+ vastTagUrl?: string; // Custom VAST URL (used in default mode if provided; when not provided, uses /ads/web endpoint)
284
+ vmapUrl?: string; // Optional VMAP 1.0 manifest URL used to schedule pre/mid/post-roll ad breaks
285
+ adPlayerType?: 'ima' | 'hls'; // Manual override for ad player type (auto-determined by vastMode if not specified)
280
286
 
281
287
  // Event handlers
282
288
  onReady?: (player: StormcloudVideoPlayer) => void;
@@ -352,6 +358,7 @@ interface StormcloudVideoPlayerConfig {
352
358
  muted?: boolean; // Start muted (default: false)
353
359
  allowNativeHls?: boolean; // Use native HLS when available (default: false)
354
360
  showCustomControls?: boolean; // Enable enhanced UI controls (default: false)
361
+ hideLoadingIndicator?: boolean; // Hide the built-in loading spinner (default: false)
355
362
  lowLatencyMode?: boolean; // Enable low-latency mode for live streams (default: false)
356
363
  driftToleranceMs?: number; // Drift tolerance for live streams (default: 1000)
357
364
  immediateManifestAds?: boolean; // Load ads immediately from manifest (default: true)
@@ -360,10 +367,15 @@ interface StormcloudVideoPlayerConfig {
360
367
  adFailsafeTimeoutMs?: number; // Ad timeout in milliseconds (default: 10000)
361
368
  minSegmentsBeforePlay?: number; // Number of segments to buffer before starting playback (default: 2)
362
369
 
370
+ // Ad break timing
371
+ adBreakCheckIntervalMs?: number; // Interval used to re-check an active ad break (default: 1000, min: 250)
372
+ maxAdBreakExtensionMs?: number; // Max time an ad break may be extended past its SCTE-35 duration when ads are still playing/queued (default: 60000)
373
+
363
374
  // Ad configuration
364
- vastMode?: 'adstorm' | 'default'; // VAST mode (default: 'default')
365
- vastTagUrl?: string; // Custom VAST tag URL (if provided)
366
- prebid?: PrebidConfig; // Prebid Server configuration for ad auctions
375
+ vastMode?: 'adstorm' | 'default'; // VAST mode: 'adstorm' (uses HLS player + AdStorm VAST endpoint) or 'default' (uses Google IMA SDK + /ads/web endpoint) (default: 'default')
376
+ vastTagUrl?: string; // Custom VAST tag URL (used in default mode if provided; when not provided, defaults to /ads/web endpoint)
377
+ vmapUrl?: string; // Optional VMAP 1.0 manifest URL used to schedule pre/mid/post-roll ad breaks
378
+ adPlayerType?: 'ima' | 'hls'; // Manual override for ad player type (auto-determined by vastMode/browser if not specified)
367
379
 
368
380
  onVolumeToggle?: () => void; // Callback for volume toggle
369
381
  onFullscreenToggle?: () => void; // Callback for fullscreen toggle
@@ -446,7 +458,7 @@ const player = new StormcloudVideoPlayer({
446
458
  ```
447
459
 
448
460
  **What happens:**
449
- - 🎯 Ad playback via Prebid Controller
461
+ - 🎯 Automatically uses HLS ad player (`adPlayerType: 'hls'`)
450
462
  - 🔗 VAST endpoint: `GET https://adstorm.co/api-adstorm-dev/adstorm/vast/{licenseKey}`
451
463
  - License key is passed in the URL path (no authorization header needed)
452
464
  - Returns VAST XML directly with HLS media files
@@ -460,7 +472,7 @@ const player = new StormcloudVideoPlayer({
460
472
  4. HLS ad player loads and plays the ad segments
461
473
 
462
474
  **Benefits:**
463
- - ✅ Zero external ad SDK dependencies (Prebid Server only)
475
+ - ✅ Zero external dependencies (no Google IMA SDK)
464
476
  - ✅ Full control over ad serving
465
477
  - ✅ Native HLS playback (same format as content)
466
478
  - ✅ Better performance and reliability
@@ -469,7 +481,7 @@ const player = new StormcloudVideoPlayer({
469
481
 
470
482
  #### 2. **Default Mode**
471
483
 
472
- Ad playback uses Prebid Server for VAST ads:
484
+ Uses Google IMA SDK for traditional ad serving:
473
485
 
474
486
  ```javascript
475
487
  const player = new StormcloudVideoPlayer({
@@ -477,7 +489,7 @@ const player = new StormcloudVideoPlayer({
477
489
  src: "https://your-stream.com/playlist.m3u8",
478
490
  licenseKey: "your-license-key",
479
491
 
480
- // Uses Prebid Controller for ad playback
492
+ // Default mode - uses Google IMA SDK automatically
481
493
  vastMode: 'default', // or omit this property entirely
482
494
  vastTagUrl: 'https://your-vast-server.com/vast.xml', // optional
483
495
 
@@ -486,20 +498,20 @@ const player = new StormcloudVideoPlayer({
486
498
  ```
487
499
 
488
500
  **What happens:**
489
- - 🎯 Ad playback via Prebid Controller
501
+ - 🎯 Automatically uses Google IMA SDK (`adPlayerType: 'ima'`)
490
502
  - 🔗 VAST endpoint resolution:
491
503
  1. If `vastTagUrl` is provided, uses that URL directly
492
504
  2. Otherwise, calls `GET https://adstorm.co/api-adstorm-dev/adstorm/ads/web`
493
505
  - Requires `Authorization: Bearer {licenseKey}` header
494
- - Returns VAST URLs from your ad backend
506
+ - Returns JSON response with IMA payload: `{ response: { ima: { "publisherdesk.ima": { payload: "VAST_URL" } } } }`
495
507
  - Extracts VAST tag URL from the `payload` field
496
- - 📊 VAST ad serving through Prebid Server
508
+ - 📊 Standard VAST/VPAID ad serving through Google IMA SDK
497
509
 
498
510
  **API Flow:**
499
511
  1. Player calls `/ads/web` endpoint with Bearer token (if no `vastTagUrl` provided)
500
- 2. Backend or Prebid Server provides VAST URLs
512
+ 2. Backend returns JSON with IMA configuration
501
513
  3. Player extracts VAST tag URL from `response.ima["publisherdesk.ima"].payload`
502
- 4. Prebid Controller loads and plays the VAST ad
514
+ 4. Google IMA SDK loads and plays the VAST ad
503
515
 
504
516
  **Benefits:**
505
517
  - ✅ Industry-standard ad serving
@@ -508,186 +520,6 @@ const player = new StormcloudVideoPlayer({
508
520
  - ✅ Backward compatible with existing VAST tags
509
521
  - ✅ Supports both custom VAST URLs and AdStorm backend
510
522
 
511
- #### 3. **Prebid Mode** (Server-Side Header Bidding)
512
-
513
- Uses Prebid Server for server-side header bidding auctions. The player sends OpenRTB requests directly to a Prebid Server instance, receives bid responses with VAST creatives, and plays the winning ad — all without any external SDK.
514
-
515
- **Vanilla JavaScript:**
516
-
517
- ```javascript
518
- const player = new StormcloudVideoPlayer({
519
- videoElement: video,
520
- src: "https://your-stream.com/playlist.m3u8",
521
- licenseKey: "your-license-key",
522
-
523
- prebid: { /* PrebidConfig */ },
524
- prebid: {
525
- enabled: true,
526
- serverUrl: "https://prebid-server.example.com",
527
- timeout: 3000,
528
- debug: true,
529
- cpmFloor: 0.50,
530
- ortbRequest: {
531
- id: "my-video-player",
532
- imp: [
533
- {
534
- id: "video-imp-1",
535
- video: {
536
- w: 640,
537
- h: 480,
538
- mimes: ["video/mp4", "application/x-mpegURL"],
539
- protocols: [2, 5],
540
- minduration: 5,
541
- maxduration: 60,
542
- linearity: 1,
543
- playbackmethod: [1, 2],
544
- },
545
- ext: {
546
- prebid: {
547
- bidder: {
548
- appnexus: { placement_id: 12345 },
549
- rubicon: { account_id: 1001, site_id: 2002, zone_id: 3003 },
550
- },
551
- },
552
- },
553
- },
554
- ],
555
- tmax: 3000,
556
- site: {
557
- domain: "example.com",
558
- page: "https://example.com/video-page",
559
- },
560
- },
561
- },
562
-
563
- debugAdTiming: true,
564
- });
565
-
566
- await player.load();
567
- ```
568
-
569
- **CDN Usage:**
570
-
571
- ```html
572
- <video id="video" style="width:100%;aspect-ratio:16/9"></video>
573
-
574
- <script src="https://cdn.jsdelivr.net/npm/stormcloud-video-player/dist/stormcloud-vp.min.js"></script>
575
- <script>
576
- var StormcloudVideoPlayer = window.StormcloudVP.StormcloudVideoPlayer;
577
-
578
- var video = document.getElementById("video");
579
-
580
- var player = new StormcloudVideoPlayer({
581
- videoElement: video,
582
- src: "https://your-stream.com/playlist.m3u8",
583
- autoplay: true,
584
- muted: true,
585
- licenseKey: "your-license-key",
586
-
587
- prebid: {
588
- enabled: true,
589
- serverUrl: "https://prebid-server.example.com",
590
- timeout: 3000,
591
- debug: true,
592
- cpmFloor: 0.50,
593
- ortbRequest: {
594
- id: "my-video-player",
595
- imp: [
596
- {
597
- id: "video-imp-1",
598
- video: {
599
- w: 640,
600
- h: 480,
601
- mimes: ["video/mp4", "application/x-mpegURL"],
602
- protocols: [2, 5],
603
- minduration: 5,
604
- maxduration: 60,
605
- linearity: 1,
606
- playbackmethod: [1, 2],
607
- },
608
- ext: {
609
- prebid: {
610
- bidder: {
611
- appnexus: { placement_id: 12345 },
612
- },
613
- },
614
- },
615
- },
616
- ],
617
- tmax: 3000,
618
- },
619
- },
620
-
621
- debugAdTiming: true,
622
- });
623
-
624
- player.load();
625
- </script>
626
- ```
627
-
628
- **What happens:**
629
- - Sends OpenRTB 2.5 auction requests directly to your Prebid Server
630
- - Receives bid responses with VAST URLs (cached via Prebid Server) or inline VAST XML
631
- - Automatically selects the highest-CPM winning bid
632
- - Enforces CPM floor pricing (bids below the floor are rejected)
633
- - Plays the winning ad creative natively (MP4 or HLS)
634
- - Fires all VAST tracking events (impression, quartiles, complete, error, pause/resume, mute/unmute)
635
-
636
- **Auction Flow:**
637
- 1. Player builds an OpenRTB request with device/site info auto-populated from the browser
638
- 2. Request is sent to `{serverUrl}/openrtb2/auction` via POST
639
- 3. Prebid Server runs the server-side auction across all configured bidders
640
- 4. Player parses the response, selects the highest-CPM bid
641
- 5. VAST creative (cached URL or inline XML) is loaded and played
642
-
643
- **Benefits:**
644
- - Zero external SDK dependencies (no Google IMA, no Prebid.js client-side library)
645
- - Full server-side auction — lower client-side resource usage
646
- - Support for any Prebid Server bidder (AppNexus, Rubicon, Index Exchange, etc.)
647
- - CPM floor enforcement on the client side
648
- - Native VAST ad playback with full tracking support
649
- - Works on all platforms including Smart TVs and legacy browsers
650
- - Complete OpenRTB 2.5 compliance
651
-
652
- ### PrebidConfig Reference
653
-
654
- ```typescript
655
- interface PrebidConfig {
656
- enabled?: boolean; // Enable/disable prebid (default: true)
657
- serverUrl?: string; // Prebid Server URL (e.g. "https://prebid-server.example.com")
658
- ortbRequest: { // OpenRTB 2.5 request template
659
- id: string; // Request ID (will be made unique per auction)
660
- imp: [{ // Impression objects (at least one required)
661
- id: string;
662
- video?: {
663
- w?: number; // Video width
664
- h?: number; // Video height
665
- mimes?: string[]; // Supported MIME types
666
- protocols?: number[];
667
- minduration?: number;
668
- maxduration?: number;
669
- linearity?: number;
670
- playbackmethod?: number[];
671
- };
672
- ext?: {
673
- prebid?: {
674
- bidder: { // Bidder configurations
675
- [bidderName: string]: Record<string, any>;
676
- };
677
- };
678
- };
679
- }];
680
- tmax?: number; // Auction timeout in ms (default: 3000)
681
- site?: Record<string, any>; // Auto-populated from window.location if omitted
682
- device?: Record<string, any>; // Auto-populated from navigator if omitted
683
- ext?: Record<string, any>;
684
- };
685
- timeout?: number; // Client-side fetch timeout in ms (default: 3000)
686
- debug?: boolean; // Enable debug logging (default: false)
687
- cpmFloor?: number; // Minimum CPM to accept a bid (default: 0, accepts all)
688
- }
689
- ```
690
-
691
523
  ### Ad Pod Generation (Multiple Consecutive Ads)
692
524
 
693
525
  The player automatically generates **ad pods** (multiple ads played consecutively) from a single VAST URL. This works differently for VOD and live streams:
@@ -806,6 +638,94 @@ Final: 3 ads played (perfectly fills 120 seconds)
806
638
  - ✅ **Smart**: Improves calculation as more data is gathered (uses average of fetched durations)
807
639
  - ✅ **Self-Correcting**: Automatically adjusts if actual ad lengths differ from expectations
808
640
 
641
+ ### VMAP Ad Break Scheduling
642
+
643
+ In addition to SCTE-35 markers and continuous-ad-fetch, the player supports **VMAP 1.0** manifests for scheduling ad breaks at specific positions in the timeline. This is ideal for VOD content where you want to declaratively define pre-roll, mid-roll, and post-roll breaks with explicit time offsets.
644
+
645
+ #### Usage
646
+
647
+ Provide a `vmapUrl` in addition to (or instead of) a VAST/AdStorm configuration:
648
+
649
+ ```javascript
650
+ const player = new StormcloudVideoPlayer({
651
+ videoElement: video,
652
+ src: "https://your-stream.com/video.m3u8",
653
+ licenseKey: "your-license-key",
654
+
655
+ vastMode: 'default',
656
+ vmapUrl: 'https://your-cdn.com/schedule.vmap', // VMAP 1.0 manifest
657
+
658
+ debugAdTiming: true,
659
+ });
660
+ ```
661
+
662
+ Or with React:
663
+
664
+ ```jsx
665
+ <StormcloudPlayer
666
+ src="https://your-stream.com/video.m3u8"
667
+ licenseKey="your-license-key"
668
+ vmapUrl="https://your-cdn.com/schedule.vmap"
669
+ playing={true}
670
+ />
671
+ ```
672
+
673
+ #### Supported `timeOffset` Formats
674
+
675
+ The VMAP parser recognizes all standard `timeOffset` values:
676
+
677
+ | Value | Meaning |
678
+ | --------------------- | ---------------------------------------------------- |
679
+ | `start` | Pre-roll (playback position 0) |
680
+ | `end` | Post-roll (resolved at runtime using media duration) |
681
+ | `HH:MM:SS` / `HH:MM:SS.mmm` | Absolute timestamp mid-roll |
682
+ | `NN%` | Percentage of media duration (resolved at runtime) |
683
+
684
+ #### Example VMAP Manifest
685
+
686
+ ```xml
687
+ <?xml version="1.0" encoding="UTF-8"?>
688
+ <vmap:VMAP xmlns:vmap="http://www.iab.net/videosuite/vmap" version="1.0">
689
+ <vmap:AdBreak timeOffset="start" breakType="linear" breakId="preroll">
690
+ <vmap:AdSource>
691
+ <vmap:AdTagURI templateType="vast3">
692
+ <![CDATA[https://your-ad-server.com/vast?position=preroll]]>
693
+ </vmap:AdTagURI>
694
+ </vmap:AdSource>
695
+ </vmap:AdBreak>
696
+
697
+ <vmap:AdBreak timeOffset="00:05:00" breakType="linear" breakId="midroll-1">
698
+ <vmap:AdSource>
699
+ <vmap:AdTagURI templateType="vast3">
700
+ <![CDATA[https://your-ad-server.com/vast?position=midroll-1]]>
701
+ </vmap:AdTagURI>
702
+ </vmap:AdSource>
703
+ </vmap:AdBreak>
704
+
705
+ <vmap:AdBreak timeOffset="end" breakType="linear" breakId="postroll">
706
+ <vmap:AdSource>
707
+ <vmap:AdTagURI templateType="vast3">
708
+ <![CDATA[https://your-ad-server.com/vast?position=postroll]]>
709
+ </vmap:AdTagURI>
710
+ </vmap:AdSource>
711
+ </vmap:AdBreak>
712
+ </vmap:VMAP>
713
+ ```
714
+
715
+ #### How It Works
716
+
717
+ 1. The player fetches the VMAP manifest on load (before playback starts).
718
+ 2. Each `<AdBreak>` is parsed into an `AdBreak` object with a resolved `startTimeMs` and the associated `vastTagUrl` from `<AdTagURI>`.
719
+ 3. During playback, the player matches the current time against each scheduled break and triggers the ad request at the correct offset.
720
+ 4. **Mid-roll join behavior**: If the viewer joins mid-stream and has already passed a scheduled break, the player will honor the configured late-join policy (e.g., play the remaining portion or skip to content).
721
+ 5. Each break is only consumed once per session to avoid replaying breaks after seeking.
722
+
723
+ **Benefits:**
724
+ - ✅ Declarative ad scheduling — no custom code needed per break
725
+ - ✅ Works alongside `vastMode: 'adstorm'` and `vastMode: 'default'`
726
+ - ✅ Supports pre-roll, mid-roll, percentage-based, and post-roll breaks
727
+ - ✅ Gracefully handles malformed XML and fetch failures (logged when `debugAdTiming` is enabled)
728
+
809
729
  ### Manual Ad Player Override
810
730
 
811
731
  You can still manually override the ad player type if needed:
@@ -816,13 +736,16 @@ const player = new StormcloudVideoPlayer({
816
736
  src: "https://your-stream.com/playlist.m3u8",
817
737
 
818
738
  vastMode: 'default',
819
- vastTagUrl: 'https://your-backend.com/vast', // Optional custom VAST URL
739
+ adPlayerType: 'hls', // Manual override to use HLS player with default mode
740
+ vastTagUrl: 'https://your-backend.com/vast', // Will use this URL directly
820
741
 
821
742
  debugAdTiming: true,
822
743
  });
823
744
  ```
824
745
 
825
- **Note:** The player uses Prebid Controller for ad playback. Provide `prebid` config for Prebid Server auctions.
746
+ **Note:** When `adPlayerType` is manually set, the `vastMode` property still determines which backend endpoint is called:
747
+ - `vastMode: 'adstorm'` → Always calls `/vast/{licenseKey}` endpoint
748
+ - `vastMode: 'default'` → Calls `/ads/web` endpoint (unless `vastTagUrl` is provided)
826
749
 
827
750
  ### SCTE-35 Support
828
751
 
@@ -996,11 +919,57 @@ Authenticated requests are sent to:
996
919
 
997
920
  - **Default Mode** (`vastMode: 'default'`):
998
921
  - Ad configuration: `GET https://adstorm.co/api-adstorm-dev/adstorm/ads/web` (requires `Authorization: Bearer {licenseKey}` header)
999
- - Returns VAST tag URL or Prebid Server provides creatives
922
+ - Returns JSON with IMA payload containing VAST tag URL
1000
923
 
1001
924
  - **Player Tracking** (both modes):
1002
925
  - Player tracking: `POST https://adstorm.co/api-adstorm-dev/adstorm/player-tracking/track` (requires `Authorization: Bearer {licenseKey}` header)
1003
926
  - Heartbeat monitoring: `POST https://adstorm.co/api-adstorm-dev/adstorm/player-tracking/heartbeat` (requires `Authorization: Bearer {licenseKey}` header)
927
+ - Ad-detect, ad-loaded, and ad-impression events are automatically emitted during ad breaks.
928
+
929
+ ### Tracking Utilities
930
+
931
+ The player exposes low-level tracking helpers if you want to fire events manually or build custom analytics:
932
+
933
+ ```javascript
934
+ import {
935
+ getClientInfo,
936
+ getBrowserID,
937
+ sendInitialTracking,
938
+ sendHeartbeat,
939
+ sendAdDetectTracking,
940
+ sendAdLoadedTracking,
941
+ sendAdImpressionTracking,
942
+ } from "stormcloud-video-player";
943
+
944
+ const clientInfo = getClientInfo();
945
+ const browserId = getBrowserID();
946
+
947
+ await sendInitialTracking(licenseKey, clientInfo);
948
+ await sendHeartbeat(licenseKey, { browserId, timestamp: new Date().toISOString() });
949
+
950
+ // Ad lifecycle tracking
951
+ await sendAdDetectTracking(licenseKey, {
952
+ source: "scte35",
953
+ durationSeconds: 30,
954
+ ptsSeconds: 120.5,
955
+ detectedAtFragmentSn: 1234,
956
+ timestamp: new Date().toISOString(),
957
+ });
958
+
959
+ await sendAdLoadedTracking(licenseKey, {
960
+ source: "ima", // 'prebid' | 'ima' | 'hls'
961
+ vastUrl: "https://...",
962
+ durationSeconds: 15,
963
+ timestamp: new Date().toISOString(),
964
+ });
965
+
966
+ await sendAdImpressionTracking(licenseKey, {
967
+ source: "ima",
968
+ adIndex: 0,
969
+ durationSeconds: 15,
970
+ timestamp: new Date().toISOString(),
971
+ });
972
+ ```
1004
973
 
1005
974
  ## 🔧 Advanced Configuration
1006
975
 
@@ -1109,11 +1078,74 @@ console.log("Supports PIP:", canPIP); // true for file player
1109
1078
  />
1110
1079
  ```
1111
1080
 
1112
- ## 🌐 Browser Support
1081
+ ## 🌐 Browser & Smart TV Support
1113
1082
 
1114
1083
  - **Desktop**: Chrome 60+, Firefox 55+, Safari 12+, Edge 79+
1115
1084
  - **Mobile**: iOS Safari 12+, Chrome Mobile 60+
1116
- - **Smart TV**: WebOS, Tizen, Android TV, Roku, Apple TV
1085
+ - **Smart TV**: LG WebOS (v2+), Samsung Tizen (v2+), Sony BRAVIA, Android TV, Roku, Apple TV, generic Smart TV user-agents
1086
+
1087
+ ### Automatic Browser Detection & Ad Player Selection
1088
+
1089
+ The player automatically detects the runtime environment and chooses the optimal ad-player configuration. This is especially useful for Smart TVs where the Google IMA SDK may not be available on older devices.
1090
+
1091
+ ```javascript
1092
+ import {
1093
+ detectBrowser,
1094
+ supportsGoogleIMA,
1095
+ getRecommendedAdPlayer,
1096
+ supportsModernJS,
1097
+ logBrowserInfo,
1098
+ getBrowserConfigOverrides,
1099
+ supportsFeature,
1100
+ } from "stormcloud-video-player";
1101
+
1102
+ const info = detectBrowser();
1103
+ // {
1104
+ // name: 'LG WebOS',
1105
+ // version: '5.0',
1106
+ // majorVersion: 5,
1107
+ // isSmartTV: true,
1108
+ // isLegacyTV: false,
1109
+ // supportsIMA: true,
1110
+ // supportsModernJS: true,
1111
+ // recommendedAdPlayer: 'ima',
1112
+ // webOSVersion: 5,
1113
+ // chromeVersion: 79,
1114
+ // ...
1115
+ // }
1116
+
1117
+ console.log(supportsGoogleIMA()); // true | false
1118
+ console.log(getRecommendedAdPlayer()); // 'ima' | 'hls'
1119
+ console.log(supportsFeature('fetch')); // true | false
1120
+ ```
1121
+
1122
+ **What happens automatically:**
1123
+
1124
+ - **Legacy Smart TVs** (LG NetCast, old WebOS < 3, old Tizen): `adPlayerType` is forced to `'hls'` and `allowNativeHls` is enabled.
1125
+ - **Modern Smart TVs**: `allowNativeHls` is enabled by default (TVs generally play HLS better natively).
1126
+ - **If IMA SDK is unsupported**: the player falls back to the HLS ad player automatically.
1127
+ - **Browser overrides** are merged with your config — user-provided options always take precedence.
1128
+
1129
+ Call `logBrowserInfo(true)` (or enable `debugAdTiming`) to print a detailed compatibility report to the console.
1130
+
1131
+ ### Automatic Polyfills for Legacy Devices
1132
+
1133
+ On older Smart TVs and browsers, `initializePolyfills()` is invoked automatically at construction time. It adds missing primitives needed for the player to run:
1134
+
1135
+ - `URLSearchParams`
1136
+ - `TextEncoder`
1137
+ - `Promise.prototype.finally`
1138
+ - `Object.assign`
1139
+ - `Array.from`
1140
+ - `String.prototype.startsWith` / `endsWith` / `includes`
1141
+
1142
+ You can also run it manually before bootstrapping anything else:
1143
+
1144
+ ```javascript
1145
+ import { initializePolyfills } from "stormcloud-video-player";
1146
+
1147
+ initializePolyfills();
1148
+ ```
1117
1149
 
1118
1150
  ### Format Support by Player
1119
1151
 
@@ -1162,18 +1194,19 @@ src/
1162
1194
  │ ├── HlsPlayer.tsx # HLS stream handler
1163
1195
  │ └── FilePlayer.tsx # Regular video handler
1164
1196
  ├── player/
1165
- │ └── StormcloudVideoPlayer.ts # Core player class
1197
+ │ └── StormcloudVideoPlayer.ts # Core player class (SCTE-35, VMAP, ad pods)
1166
1198
  ├── ui/
1167
1199
  │ └── StormcloudVideoPlayer.tsx # Legacy React component
1168
1200
  ├── sdk/
1169
- │ ├── prebid.ts # Prebid Server auction manager
1170
- ├── prebidController.ts # Prebid ad controller (VAST playback + tracking)
1171
- │ └── vastParser.ts # VAST XML parser
1201
+ │ ├── ima.ts # Google IMA integration
1202
+ └── hlsAdPlayer.ts # Native HLS ad player (AdStorm mode, legacy TVs)
1172
1203
  ├── utils/
1173
- │ ├── tracking.ts # Analytics and tracking
1174
- └── index.ts # Utility functions
1204
+ │ ├── tracking.ts # Analytics and ad tracking
1205
+ ├── browserCompat.ts # Browser / Smart TV detection & auto-overrides
1206
+ │ └── polyfills.ts # Legacy browser polyfills
1175
1207
  ├── props.ts # Centralized props system
1176
1208
  ├── patterns.ts # URL pattern matching
1209
+ ├── utils.ts # Shared utilities
1177
1210
  └── types.ts # TypeScript definitions
1178
1211
  ```
1179
1212
 
@@ -1288,14 +1321,19 @@ Built with ❤️ by the Stormcloud team
1288
1321
 
1289
1322
  ### What's New in v0.5
1290
1323
 
1291
- - **Prebid Server Integration**: Server-side header bidding via Prebid Server (OpenRTB 2.5)
1292
- - **Zero-SDK Ad Auctions**: Run server-side auctions across multiple bidders (AppNexus, Rubicon, Index Exchange, etc.) without any client-side ad SDK
1293
- - **PrebidConfig**: Full OpenRTB 2.5 request configuration with auto-populated device/site fields
1294
- - **CPM Floor Enforcement**: Client-side floor pricing to reject bids below a minimum CPM threshold
1295
- - **Native VAST Playback**: Winning bid creatives (cached VAST URL or inline VAST XML) played natively with full tracking (impression, quartiles, complete, pause/resume, mute/unmute, error)
1296
- - **Smart Media Selection**: Automatic selection of the best ad media file based on the main stream's current resolution and bitrate
1297
- - **HLS Ad Creatives**: Support for HLS-format ad creatives alongside MP4, with automatic format detection
1298
- - **CDN-Ready**: Full prebid support available via CDN (`window.StormcloudVP.StormcloudVideoPlayer`) with no build step required
1324
+ - 🗓️ **VMAP 1.0 Support**: New `vmapUrl` config/prop loads a VMAP manifest and schedules pre-roll, mid-roll, percentage-based, and post-roll breaks automatically
1325
+ - Supports `start`, `end`, `HH:MM:SS[.mmm]`, and `NN%` `timeOffset` values
1326
+ - Namespaced (`vmap:AdBreak`) and non-namespaced manifests both supported
1327
+ - Breaks are consumed-once-per-session and integrate with the existing late-join policy
1328
+ - 📺 **Smart TV First-Class Support**: New browser-compat layer auto-detects LG WebOS, Samsung Tizen, Sony BRAVIA, LG NetCast and generic Smart TV UAs
1329
+ - Exports `detectBrowser`, `supportsGoogleIMA`, `getRecommendedAdPlayer`, `supportsModernJS`, `logBrowserInfo`, `getBrowserConfigOverrides`, `supportsFeature`
1330
+ - Automatically forces HLS ad player on legacy TVs where IMA SDK is not available
1331
+ - Automatically enables `allowNativeHls` on Smart TVs for more reliable playback
1332
+ - 🧩 **Automatic Polyfills**: `initializePolyfills()` runs at construction time and backfills `URLSearchParams`, `TextEncoder`, `Promise.prototype.finally`, `Object.assign`, `Array.from`, and `String.prototype.startsWith/endsWith/includes` for legacy environments
1333
+ - 📊 **Expanded Ad Tracking**: New `sendAdDetectTracking`, `sendAdLoadedTracking`, and `sendAdImpressionTracking` helpers (plus `AdDetectInfo`, `AdLoadedInfo`, `AdImpressionInfo`, and `AdTrackingSource` types) for prebid/IMA/HLS ad lifecycle events
1334
+ - ⚙️ **Ad-Break Timing Controls**: New `adBreakCheckIntervalMs` (default 1000ms, min 250ms) and `maxAdBreakExtensionMs` (default 60000ms) options give you precise control over how long the player is allowed to extend an ad break past its SCTE-35 duration when ads are still playing or queued
1335
+ - 🙈 **Hide Loading Indicator**: New `hideLoadingIndicator` prop/config hides the built-in buffering spinner when you want to render your own overlay
1336
+ - 🔌 **Expanded Public API**: `createImaController`, `createHlsAdPlayer`, and `initializePolyfills` are now exported alongside the new browser-compat utilities, making it easier to build custom integrations on top of the player core
1299
1337
 
1300
1338
  ### What's New in v0.4
1301
1339
 
@@ -1320,14 +1358,17 @@ Built with ❤️ by the Stormcloud team
1320
1358
 
1321
1359
  ### What's New in v0.3
1322
1360
 
1323
- - 🎬 **Prebid Ad Controller**: VAST ad playback via Prebid Server
1324
- - 🎯 **Ad Integration**: Prebid Server for server-side header bidding
1361
+ - 🎬 **Custom HLS Ad Player**: Native HLS ad playback with custom VAST service
1362
+ - 🎯 **Flexible Ad Integration**: Choose between custom HLS or Google IMA SDK
1325
1363
  - 📊 **Direct Analytics**: Full control over ad tracking and metrics
1326
- - ⚡ **Better Performance**: Native VAST playback for ads
1364
+ - ⚡ **Better Performance**: Native HLS playback for ads (same format as content)
1327
1365
  - 🔧 **Custom VAST URLs**: Point to your own ad serving backend
1328
- - 📦 **Zero Dependencies**: No external ad SDKs (Prebid Server only)
1366
+ - 🔄 **Backward Compatible**: Existing Google IMA integration still works
1367
+ - 📦 **Zero Dependencies**: No external ad SDKs required (when using HLS ad player)
1329
1368
  - 🎨 **Seamless Playback**: Same player for content and ads
1330
- - 🔀 **VAST Mode**: `vastMode` property for endpoint configuration
1369
+ - 🔀 **VAST Mode System**: `vastMode` property automatically configures endpoints and ad players
1370
+ - `vastMode: 'adstorm'` → Uses `/vast/{licenseKey}` endpoint with HLS ad player
1371
+ - `vastMode: 'default'` → Uses `/ads/web` endpoint with Google IMA SDK
1331
1372
  - ⚠️ **Improved Error Handling**: Distinguishes between parsing errors and "no ads available" scenarios
1332
1373
  - Logs warnings for "no ads available" (graceful handling)
1333
1374
  - Logs errors for actual parsing/fetch failures