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.
- package/README.md +269 -228
- package/dist/stormcloud-vp.min.js +1 -1
- package/lib/index.cjs +5197 -6747
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +196 -195
- package/lib/index.d.ts +196 -195
- package/lib/index.js +5321 -6863
- package/lib/index.js.map +1 -1
- package/lib/player/StormcloudVideoPlayer.cjs +4802 -3170
- package/lib/player/StormcloudVideoPlayer.cjs.map +1 -1
- package/lib/player/StormcloudVideoPlayer.d.cts +113 -70
- package/lib/players/HlsPlayer.cjs +4810 -3179
- package/lib/players/HlsPlayer.cjs.map +1 -1
- package/lib/players/HlsPlayer.d.cts +1 -1
- package/lib/players/index.cjs +4877 -3246
- package/lib/players/index.cjs.map +1 -1
- package/lib/sdk/hlsAdPlayer.cjs +1021 -0
- package/lib/sdk/hlsAdPlayer.cjs.map +1 -0
- package/lib/sdk/hlsAdPlayer.d.cts +10 -0
- package/lib/sdk/pal.cjs +358 -0
- package/lib/sdk/pal.cjs.map +1 -0
- package/lib/sdk/pal.d.cts +28 -0
- package/lib/{types-CIHDHY7A.d.cts → types-DDwAfBLt.d.cts} +11 -10
- package/lib/ui/StormcloudVideoPlayer.cjs +5070 -6609
- package/lib/ui/StormcloudVideoPlayer.cjs.map +1 -1
- package/lib/ui/StormcloudVideoPlayer.d.cts +1 -14
- package/lib/utils/browserCompat.cjs +10 -114
- package/lib/utils/browserCompat.cjs.map +1 -1
- package/lib/utils/browserCompat.d.cts +3 -7
- package/lib/utils/tracking.cjs +179 -150
- package/lib/utils/tracking.cjs.map +1 -1
- package/lib/utils/tracking.d.cts +11 -6
- package/package.json +1 -2
- package/lib/sdk/adstormPlayer.cjs +0 -1567
- package/lib/sdk/adstormPlayer.cjs.map +0 -1
- package/lib/sdk/adstormPlayer.d.cts +0 -23
- package/lib/sdk/vastParser.cjs +0 -517
- package/lib/sdk/vastParser.cjs.map +0 -1
- package/lib/sdk/vastParser.d.cts +0 -45
- package/lib/ui/OverlayRenderer.cjs +0 -2376
- package/lib/ui/OverlayRenderer.cjs.map +0 -1
- package/lib/ui/OverlayRenderer.d.cts +0 -17
- package/lib/utils/adcision.cjs +0 -282
- package/lib/utils/adcision.cjs.map +0 -1
- package/lib/utils/adcision.d.cts +0 -31
- package/lib/utils/overlays.cjs +0 -354
- package/lib/utils/overlays.cjs.map +0 -1
- 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,
|
|
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
|
|
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
|
|
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
|
-
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
- 🎯
|
|
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
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
-
- 🎯
|
|
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
|
|
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
|
|
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
|
|
512
|
+
2. Backend returns JSON with IMA configuration
|
|
501
513
|
3. Player extracts VAST tag URL from `response.ima["publisherdesk.ima"].payload`
|
|
502
|
-
4.
|
|
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
|
-
|
|
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:**
|
|
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
|
|
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
|
-
│ ├──
|
|
1170
|
-
│
|
|
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
|
-
│
|
|
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
|
-
- **
|
|
1292
|
-
-
|
|
1293
|
-
-
|
|
1294
|
-
-
|
|
1295
|
-
- **
|
|
1296
|
-
-
|
|
1297
|
-
-
|
|
1298
|
-
-
|
|
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
|
-
- 🎬 **
|
|
1324
|
-
- 🎯 **Ad Integration**:
|
|
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
|
|
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
|
-
-
|
|
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
|
|
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
|