stormcloud-video-player 0.2.35 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +118 -0
- package/dist/stormcloud-vp.min.js +2 -2
- package/lib/index.cjs +234 -60
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +8 -3
- package/lib/index.d.ts +8 -3
- package/lib/index.js +234 -60
- package/lib/index.js.map +1 -1
- package/lib/player/StormcloudVideoPlayer.cjs +234 -60
- package/lib/player/StormcloudVideoPlayer.cjs.map +1 -1
- package/lib/player/StormcloudVideoPlayer.d.cts +8 -3
- package/lib/players/HlsPlayer.cjs +234 -60
- package/lib/players/HlsPlayer.cjs.map +1 -1
- package/lib/players/index.cjs +234 -60
- package/lib/players/index.cjs.map +1 -1
- package/lib/sdk/ima.cjs +1 -10
- package/lib/sdk/ima.cjs.map +1 -1
- package/lib/ui/StormcloudVideoPlayer.cjs +234 -60
- package/lib/ui/StormcloudVideoPlayer.cjs.map +1 -1
- package/package.json +1 -1
package/lib/index.d.cts
CHANGED
|
@@ -116,6 +116,7 @@ declare class StormcloudVideoPlayer {
|
|
|
116
116
|
private ptsDriftEmaMs;
|
|
117
117
|
private adPodQueue;
|
|
118
118
|
private apiVastTagUrl;
|
|
119
|
+
private apiNumberAds;
|
|
119
120
|
private lastHeartbeatTime;
|
|
120
121
|
private heartbeatInterval;
|
|
121
122
|
private currentAdIndex;
|
|
@@ -137,6 +138,9 @@ declare class StormcloudVideoPlayer {
|
|
|
137
138
|
private adRequestWatchdogId;
|
|
138
139
|
private adRequestWatchdogToken;
|
|
139
140
|
private adFailsafeToken;
|
|
141
|
+
private fetchedAdDurations;
|
|
142
|
+
private targetAdBreakDurationMs;
|
|
143
|
+
private isAdaptiveMode;
|
|
140
144
|
constructor(config: StormcloudVideoPlayerConfig);
|
|
141
145
|
private createAdPlayer;
|
|
142
146
|
load(): Promise<void>;
|
|
@@ -157,6 +161,7 @@ declare class StormcloudVideoPlayer {
|
|
|
157
161
|
private fetchAdConfiguration;
|
|
158
162
|
getCurrentAdIndex(): number;
|
|
159
163
|
getTotalAdsInBreak(): number;
|
|
164
|
+
private generateVastUrlsWithCorrelators;
|
|
160
165
|
isAdPlaying(): boolean;
|
|
161
166
|
isShowingAds(): boolean;
|
|
162
167
|
getStreamType(): "hls" | "other";
|
|
@@ -181,12 +186,12 @@ declare class StormcloudVideoPlayer {
|
|
|
181
186
|
private startAdFailsafeTimer;
|
|
182
187
|
private clearAdFailsafeTimer;
|
|
183
188
|
private selectVastTagsForBreak;
|
|
184
|
-
private logQueuedAdUrls;
|
|
185
189
|
private logAdState;
|
|
186
|
-
private enforceAdHoldState;
|
|
187
|
-
private releaseAdHoldState;
|
|
188
190
|
private fetchAndParseVastXml;
|
|
189
191
|
private extractMediaUrlsFromVast;
|
|
192
|
+
private fetchVastDuration;
|
|
193
|
+
private calculateAdditionalAdsNeeded;
|
|
194
|
+
private addAdaptiveAdsToQueue;
|
|
190
195
|
private preloadMediaFile;
|
|
191
196
|
private preloadAllAdsInBackground;
|
|
192
197
|
private preloadSingleAd;
|
package/lib/index.d.ts
CHANGED
|
@@ -116,6 +116,7 @@ declare class StormcloudVideoPlayer {
|
|
|
116
116
|
private ptsDriftEmaMs;
|
|
117
117
|
private adPodQueue;
|
|
118
118
|
private apiVastTagUrl;
|
|
119
|
+
private apiNumberAds;
|
|
119
120
|
private lastHeartbeatTime;
|
|
120
121
|
private heartbeatInterval;
|
|
121
122
|
private currentAdIndex;
|
|
@@ -137,6 +138,9 @@ declare class StormcloudVideoPlayer {
|
|
|
137
138
|
private adRequestWatchdogId;
|
|
138
139
|
private adRequestWatchdogToken;
|
|
139
140
|
private adFailsafeToken;
|
|
141
|
+
private fetchedAdDurations;
|
|
142
|
+
private targetAdBreakDurationMs;
|
|
143
|
+
private isAdaptiveMode;
|
|
140
144
|
constructor(config: StormcloudVideoPlayerConfig);
|
|
141
145
|
private createAdPlayer;
|
|
142
146
|
load(): Promise<void>;
|
|
@@ -157,6 +161,7 @@ declare class StormcloudVideoPlayer {
|
|
|
157
161
|
private fetchAdConfiguration;
|
|
158
162
|
getCurrentAdIndex(): number;
|
|
159
163
|
getTotalAdsInBreak(): number;
|
|
164
|
+
private generateVastUrlsWithCorrelators;
|
|
160
165
|
isAdPlaying(): boolean;
|
|
161
166
|
isShowingAds(): boolean;
|
|
162
167
|
getStreamType(): "hls" | "other";
|
|
@@ -181,12 +186,12 @@ declare class StormcloudVideoPlayer {
|
|
|
181
186
|
private startAdFailsafeTimer;
|
|
182
187
|
private clearAdFailsafeTimer;
|
|
183
188
|
private selectVastTagsForBreak;
|
|
184
|
-
private logQueuedAdUrls;
|
|
185
189
|
private logAdState;
|
|
186
|
-
private enforceAdHoldState;
|
|
187
|
-
private releaseAdHoldState;
|
|
188
190
|
private fetchAndParseVastXml;
|
|
189
191
|
private extractMediaUrlsFromVast;
|
|
192
|
+
private fetchVastDuration;
|
|
193
|
+
private calculateAdditionalAdsNeeded;
|
|
194
|
+
private addAdaptiveAdsToQueue;
|
|
190
195
|
private preloadMediaFile;
|
|
191
196
|
private preloadAllAdsInBackground;
|
|
192
197
|
private preloadSingleAd;
|
package/lib/index.js
CHANGED
|
@@ -626,19 +626,10 @@ function createImaController(video, options) {
|
|
|
626
626
|
adsManager.addEventListener(
|
|
627
627
|
AdEvent.CONTENT_PAUSE_REQUESTED,
|
|
628
628
|
() => {
|
|
629
|
-
console.log("[DEBUG-FLOW] \u{1F3AF} CONTENT_PAUSE_REQUESTED - Ad
|
|
629
|
+
console.log("[DEBUG-FLOW] \u{1F3AF} CONTENT_PAUSE_REQUESTED - Ad request accepted");
|
|
630
630
|
if (!(options == null ? void 0 : options.continueLiveStreamDuringAds)) {
|
|
631
631
|
video.pause();
|
|
632
632
|
}
|
|
633
|
-
hideContentVideo();
|
|
634
|
-
if (adContainerEl) {
|
|
635
|
-
adContainerEl.style.pointerEvents = "auto";
|
|
636
|
-
adContainerEl.style.display = "flex";
|
|
637
|
-
adContainerEl.style.backgroundColor = "#000";
|
|
638
|
-
adContainerEl.offsetHeight;
|
|
639
|
-
adContainerEl.style.opacity = "1";
|
|
640
|
-
console.log("[DEBUG-LAYER] \u{1F7E1} Ad container VISIBLE");
|
|
641
|
-
}
|
|
642
633
|
adPlaying = true;
|
|
643
634
|
setAdPlayingFlag(true);
|
|
644
635
|
emit("content_pause");
|
|
@@ -2166,6 +2157,9 @@ var StormcloudVideoPlayer = class {
|
|
|
2166
2157
|
this.activeAdRequestToken = null;
|
|
2167
2158
|
this.adRequestWatchdogToken = null;
|
|
2168
2159
|
this.adFailsafeToken = null;
|
|
2160
|
+
this.fetchedAdDurations = /* @__PURE__ */ new Map();
|
|
2161
|
+
this.targetAdBreakDurationMs = null;
|
|
2162
|
+
this.isAdaptiveMode = false;
|
|
2169
2163
|
initializePolyfills();
|
|
2170
2164
|
const browserOverrides = getBrowserConfigOverrides();
|
|
2171
2165
|
this.config = { ...config, ...browserOverrides };
|
|
@@ -2471,7 +2465,7 @@ var StormcloudVideoPlayer = class {
|
|
|
2471
2465
|
this.clearAdRequestWatchdog();
|
|
2472
2466
|
this.activeAdRequestToken = null;
|
|
2473
2467
|
this.showAds = true;
|
|
2474
|
-
|
|
2468
|
+
console.log("[DEBUG-LAYER] \u{1F3AC} Layers: Main=hidden, Ad=visible, Placeholder=no");
|
|
2475
2469
|
});
|
|
2476
2470
|
this.ima.on("content_resume", () => {
|
|
2477
2471
|
console.log(`[DEBUG-POD] \u23F8\uFE0F content_resume | ad ${this.currentAdIndex}/${this.totalAdsInBreak}, queue=${this.adPodQueue.length}, remaining=${this.getRemainingAdMs()}ms`);
|
|
@@ -2651,6 +2645,12 @@ var StormcloudVideoPlayer = class {
|
|
|
2651
2645
|
});
|
|
2652
2646
|
}
|
|
2653
2647
|
if (marker.type === "start") {
|
|
2648
|
+
if (this.inAdBreak) {
|
|
2649
|
+
console.log(
|
|
2650
|
+
`[DEBUG-POD] \u26A0\uFE0F SCTE-35 start marker ignored - already in ad break (currentTime: ${this.video.currentTime})`
|
|
2651
|
+
);
|
|
2652
|
+
return;
|
|
2653
|
+
}
|
|
2654
2654
|
this.inAdBreak = true;
|
|
2655
2655
|
const durationMs = marker.durationSeconds != null ? marker.durationSeconds * 1e3 : void 0;
|
|
2656
2656
|
this.expectedAdBreakDurationMs = durationMs;
|
|
@@ -2717,6 +2717,9 @@ var StormcloudVideoPlayer = class {
|
|
|
2717
2717
|
return;
|
|
2718
2718
|
}
|
|
2719
2719
|
if (marker.type === "progress" && this.inAdBreak) {
|
|
2720
|
+
console.log(
|
|
2721
|
+
`[DEBUG-POD] \u{1F4CA} SCTE-35 progress marker (currentTime: ${this.video.currentTime})`
|
|
2722
|
+
);
|
|
2720
2723
|
if (marker.durationSeconds != null) {
|
|
2721
2724
|
this.expectedAdBreakDurationMs = marker.durationSeconds * 1e3;
|
|
2722
2725
|
}
|
|
@@ -2728,7 +2731,8 @@ var StormcloudVideoPlayer = class {
|
|
|
2728
2731
|
);
|
|
2729
2732
|
this.scheduleAdStopCountdown(remainingMs);
|
|
2730
2733
|
}
|
|
2731
|
-
if (!this.ima.isAdPlaying()) {
|
|
2734
|
+
if (!this.ima.isAdPlaying() && this.activeAdRequestToken === null) {
|
|
2735
|
+
console.log("[DEBUG-POD] \u{1F4CA} Progress marker: no ad playing, attempting to start");
|
|
2732
2736
|
const scheduled = this.findCurrentOrNextBreak(
|
|
2733
2737
|
this.video.currentTime * 1e3
|
|
2734
2738
|
);
|
|
@@ -2737,25 +2741,31 @@ var StormcloudVideoPlayer = class {
|
|
|
2737
2741
|
const first = tags[0];
|
|
2738
2742
|
const rest = tags.slice(1);
|
|
2739
2743
|
this.adPodQueue = rest;
|
|
2740
|
-
if (!this.showAds) {
|
|
2741
|
-
this.ima.updateOriginalMutedState(this.video.muted, this.video.volume);
|
|
2742
|
-
}
|
|
2743
2744
|
this.playSingleAd(first).catch(() => {
|
|
2744
2745
|
});
|
|
2745
2746
|
}
|
|
2747
|
+
} else {
|
|
2748
|
+
console.log(
|
|
2749
|
+
`[DEBUG-POD] \u{1F4CA} Progress marker: ad playing or request active (playing=${this.ima.isAdPlaying()}, token=${this.activeAdRequestToken})`
|
|
2750
|
+
);
|
|
2746
2751
|
}
|
|
2747
2752
|
return;
|
|
2748
2753
|
}
|
|
2749
2754
|
if (marker.type === "end") {
|
|
2755
|
+
console.log(
|
|
2756
|
+
`[DEBUG-POD] \u{1F3C1} SCTE-35 end marker received (currentTime: ${this.video.currentTime})`
|
|
2757
|
+
);
|
|
2750
2758
|
this.inAdBreak = false;
|
|
2751
2759
|
this.expectedAdBreakDurationMs = void 0;
|
|
2752
2760
|
this.currentAdBreakStartWallClockMs = void 0;
|
|
2753
2761
|
this.clearAdStartTimer();
|
|
2754
2762
|
this.clearAdStopTimer();
|
|
2755
2763
|
if (this.ima.isAdPlaying()) {
|
|
2764
|
+
console.log("[DEBUG-POD] \u{1F6D1} Stopping ad due to SCTE-35 end marker");
|
|
2756
2765
|
this.ima.stop().catch(() => {
|
|
2757
2766
|
});
|
|
2758
2767
|
}
|
|
2768
|
+
this.handleAdPodComplete();
|
|
2759
2769
|
return;
|
|
2760
2770
|
}
|
|
2761
2771
|
}
|
|
@@ -2956,7 +2966,7 @@ var StormcloudVideoPlayer = class {
|
|
|
2956
2966
|
}
|
|
2957
2967
|
}
|
|
2958
2968
|
async fetchAdConfiguration() {
|
|
2959
|
-
var _a, _b, _c;
|
|
2969
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
2960
2970
|
const vastMode = this.config.vastMode || "default";
|
|
2961
2971
|
if (this.config.debugAdTiming) {
|
|
2962
2972
|
console.log("[StormcloudVideoPlayer] VAST mode:", vastMode);
|
|
@@ -3027,6 +3037,16 @@ var StormcloudVideoPlayer = class {
|
|
|
3027
3037
|
);
|
|
3028
3038
|
}
|
|
3029
3039
|
}
|
|
3040
|
+
const numberAds = (_g = (_f = (_e = (_d = data.response) == null ? void 0 : _d.options) == null ? void 0 : _e.vast) == null ? void 0 : _f.cue_tones) == null ? void 0 : _g.number_ads;
|
|
3041
|
+
if (numberAds != null && numberAds > 0) {
|
|
3042
|
+
this.apiNumberAds = numberAds;
|
|
3043
|
+
if (this.config.debugAdTiming) {
|
|
3044
|
+
console.log(
|
|
3045
|
+
"[StormcloudVideoPlayer] Number of ads per break from API:",
|
|
3046
|
+
this.apiNumberAds
|
|
3047
|
+
);
|
|
3048
|
+
}
|
|
3049
|
+
}
|
|
3030
3050
|
}
|
|
3031
3051
|
getCurrentAdIndex() {
|
|
3032
3052
|
return this.currentAdIndex;
|
|
@@ -3034,6 +3054,27 @@ var StormcloudVideoPlayer = class {
|
|
|
3034
3054
|
getTotalAdsInBreak() {
|
|
3035
3055
|
return this.totalAdsInBreak;
|
|
3036
3056
|
}
|
|
3057
|
+
generateVastUrlsWithCorrelators(baseUrl, count) {
|
|
3058
|
+
const urls = [];
|
|
3059
|
+
for (let i = 0; i < count; i++) {
|
|
3060
|
+
try {
|
|
3061
|
+
const url = new URL(baseUrl);
|
|
3062
|
+
const timestamp = Date.now();
|
|
3063
|
+
const random = Math.floor(Math.random() * 1e6);
|
|
3064
|
+
const uniqueCorrelator = `${timestamp}${random}${i}`;
|
|
3065
|
+
url.searchParams.set("correlator", uniqueCorrelator);
|
|
3066
|
+
urls.push(url.toString());
|
|
3067
|
+
} catch (error) {
|
|
3068
|
+
console.warn(
|
|
3069
|
+
"[StormcloudVideoPlayer] Failed to parse VAST URL:",
|
|
3070
|
+
baseUrl,
|
|
3071
|
+
error
|
|
3072
|
+
);
|
|
3073
|
+
urls.push(`${baseUrl}${baseUrl.includes("?") ? "&" : "?"}correlator=${Date.now()}${i}`);
|
|
3074
|
+
}
|
|
3075
|
+
}
|
|
3076
|
+
return urls;
|
|
3077
|
+
}
|
|
3037
3078
|
isAdPlaying() {
|
|
3038
3079
|
return this.inAdBreak && this.ima.isAdPlaying();
|
|
3039
3080
|
}
|
|
@@ -3072,7 +3113,52 @@ var StormcloudVideoPlayer = class {
|
|
|
3072
3113
|
const tags = this.selectVastTagsForBreak(scheduled);
|
|
3073
3114
|
let vastTagUrls = [];
|
|
3074
3115
|
if (this.apiVastTagUrl) {
|
|
3075
|
-
|
|
3116
|
+
let numberOfAds = 1;
|
|
3117
|
+
if (this.isLiveStream) {
|
|
3118
|
+
const adBreakDurationMs = _marker.durationSeconds != null ? _marker.durationSeconds * 1e3 : scheduled == null ? void 0 : scheduled.durationMs;
|
|
3119
|
+
if (adBreakDurationMs != null && adBreakDurationMs > 0) {
|
|
3120
|
+
this.isAdaptiveMode = true;
|
|
3121
|
+
this.targetAdBreakDurationMs = adBreakDurationMs;
|
|
3122
|
+
this.fetchedAdDurations.clear();
|
|
3123
|
+
numberOfAds = 2;
|
|
3124
|
+
if (this.config.debugAdTiming) {
|
|
3125
|
+
console.log(
|
|
3126
|
+
`[ADAPTIVE-POD] \u{1F4FA} LIVE MODE (ADAPTIVE): Target duration=${adBreakDurationMs}ms | Starting with ${numberOfAds} ads, will fetch actual durations and add more dynamically`
|
|
3127
|
+
);
|
|
3128
|
+
}
|
|
3129
|
+
} else {
|
|
3130
|
+
if (this.config.debugAdTiming) {
|
|
3131
|
+
console.warn(
|
|
3132
|
+
"[DEBUG-POD] \u26A0\uFE0F LIVE MODE: No duration available, defaulting to 1 ad"
|
|
3133
|
+
);
|
|
3134
|
+
}
|
|
3135
|
+
}
|
|
3136
|
+
} else {
|
|
3137
|
+
this.isAdaptiveMode = false;
|
|
3138
|
+
this.targetAdBreakDurationMs = null;
|
|
3139
|
+
this.fetchedAdDurations.clear();
|
|
3140
|
+
if (this.apiNumberAds && this.apiNumberAds > 1) {
|
|
3141
|
+
numberOfAds = this.apiNumberAds;
|
|
3142
|
+
if (this.config.debugAdTiming) {
|
|
3143
|
+
console.log(
|
|
3144
|
+
`[DEBUG-POD] \u{1F3AC} VOD MODE (FIXED): Using number_ads=${numberOfAds} from API`
|
|
3145
|
+
);
|
|
3146
|
+
}
|
|
3147
|
+
}
|
|
3148
|
+
}
|
|
3149
|
+
if (numberOfAds > 1) {
|
|
3150
|
+
vastTagUrls = this.generateVastUrlsWithCorrelators(
|
|
3151
|
+
this.apiVastTagUrl,
|
|
3152
|
+
numberOfAds
|
|
3153
|
+
);
|
|
3154
|
+
if (this.config.debugAdTiming) {
|
|
3155
|
+
console.log(
|
|
3156
|
+
`[DEBUG-POD] \u{1F504} Generated ${vastTagUrls.length} initial VAST URLs with unique correlators`
|
|
3157
|
+
);
|
|
3158
|
+
}
|
|
3159
|
+
} else {
|
|
3160
|
+
vastTagUrls = [this.apiVastTagUrl];
|
|
3161
|
+
}
|
|
3076
3162
|
} else if (tags && tags.length > 0) {
|
|
3077
3163
|
vastTagUrls = tags;
|
|
3078
3164
|
} else {
|
|
@@ -3086,10 +3172,12 @@ var StormcloudVideoPlayer = class {
|
|
|
3086
3172
|
this.vastToMediaUrlMap.clear();
|
|
3087
3173
|
this.preloadedMediaUrls.clear();
|
|
3088
3174
|
this.preloadingMediaUrls.clear();
|
|
3175
|
+
const currentMuted = this.video.muted;
|
|
3176
|
+
const currentVolume = this.video.volume;
|
|
3089
3177
|
console.log(
|
|
3090
|
-
`[DEBUG-AUDIO] \u{1F4BE} Capturing
|
|
3178
|
+
`[DEBUG-AUDIO] \u{1F4BE} Capturing ORIGINAL state (once) | muted=${currentMuted}, volume=${currentVolume}`
|
|
3091
3179
|
);
|
|
3092
|
-
this.ima.updateOriginalMutedState(
|
|
3180
|
+
this.ima.updateOriginalMutedState(currentMuted, currentVolume);
|
|
3093
3181
|
this.inAdBreak = true;
|
|
3094
3182
|
this.currentAdIndex = 0;
|
|
3095
3183
|
this.totalAdsInBreak = vastTagUrls.length;
|
|
@@ -3303,7 +3391,6 @@ var StormcloudVideoPlayer = class {
|
|
|
3303
3391
|
this.clearAdRequestWatchdog();
|
|
3304
3392
|
this.clearAdFailsafeTimer();
|
|
3305
3393
|
this.activeAdRequestToken = null;
|
|
3306
|
-
this.releaseAdHoldState();
|
|
3307
3394
|
this.preloadingAdUrls.clear();
|
|
3308
3395
|
this.vastToMediaUrlMap.clear();
|
|
3309
3396
|
this.preloadedMediaUrls.clear();
|
|
@@ -3320,13 +3407,18 @@ var StormcloudVideoPlayer = class {
|
|
|
3320
3407
|
this.totalAdsInBreak = 0;
|
|
3321
3408
|
this.ima.stop().catch(() => {
|
|
3322
3409
|
});
|
|
3323
|
-
const
|
|
3324
|
-
const
|
|
3325
|
-
this.video.muted = originalMutedState;
|
|
3326
|
-
this.video.volume = originalVolume;
|
|
3410
|
+
const restoredMuted = this.ima.getOriginalMutedState();
|
|
3411
|
+
const restoredVolume = this.ima.getOriginalVolume();
|
|
3327
3412
|
console.log(
|
|
3328
|
-
`[DEBUG-AUDIO] \u{1F50A}
|
|
3413
|
+
`[DEBUG-AUDIO] \u{1F50A} Audio restored by IMA | muted=${restoredMuted}, volume=${restoredVolume}`
|
|
3329
3414
|
);
|
|
3415
|
+
console.log("[DEBUG-LAYER] \u{1F3AC} Layers: Main=visible, Ad=hidden, Placeholder=no");
|
|
3416
|
+
if (this.video.muted !== restoredMuted) {
|
|
3417
|
+
this.video.muted = restoredMuted;
|
|
3418
|
+
}
|
|
3419
|
+
if (Math.abs(this.video.volume - restoredVolume) > 0.01) {
|
|
3420
|
+
this.video.volume = restoredVolume;
|
|
3421
|
+
}
|
|
3330
3422
|
if (!this.shouldContinueLiveStreamDuringAds() && this.video.paused) {
|
|
3331
3423
|
console.log("[DEBUG-FLOW] \u25B6\uFE0F Resuming main video playback");
|
|
3332
3424
|
(_a = this.video.play()) == null ? void 0 : _a.catch((error) => {
|
|
@@ -3335,20 +3427,20 @@ var StormcloudVideoPlayer = class {
|
|
|
3335
3427
|
}
|
|
3336
3428
|
}
|
|
3337
3429
|
handleAdFailure() {
|
|
3430
|
+
console.log("[DEBUG-POD] \u274C handleAdFailure - skipping to next ad or ending break");
|
|
3338
3431
|
const remaining = this.getRemainingAdMs();
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
return;
|
|
3432
|
+
if (remaining > 500 && this.adPodQueue.length > 0) {
|
|
3433
|
+
const nextPreloaded = this.findNextPreloadedAd();
|
|
3434
|
+
if (nextPreloaded) {
|
|
3435
|
+
this.currentAdIndex++;
|
|
3436
|
+
console.log(`[DEBUG-POD] \u27A1\uFE0F Trying next ad after failure (${this.currentAdIndex}/${this.totalAdsInBreak})`);
|
|
3437
|
+
this.playSingleAd(nextPreloaded).catch(() => {
|
|
3438
|
+
this.handleAdPodComplete();
|
|
3439
|
+
});
|
|
3440
|
+
return;
|
|
3441
|
+
}
|
|
3350
3442
|
}
|
|
3351
|
-
console.log("[DEBUG-POD] \u23F9\uFE0F
|
|
3443
|
+
console.log("[DEBUG-POD] \u23F9\uFE0F Ending ad break after failure");
|
|
3352
3444
|
this.handleAdPodComplete();
|
|
3353
3445
|
}
|
|
3354
3446
|
startAdRequestWatchdog(token) {
|
|
@@ -3421,12 +3513,6 @@ var StormcloudVideoPlayer = class {
|
|
|
3421
3513
|
}
|
|
3422
3514
|
return [b.vastTagUrl];
|
|
3423
3515
|
}
|
|
3424
|
-
logQueuedAdUrls(urls) {
|
|
3425
|
-
if (!this.config.debugAdTiming) {
|
|
3426
|
-
return;
|
|
3427
|
-
}
|
|
3428
|
-
console.log("[StormcloudVideoPlayer] ALL ad URLs queued:", urls);
|
|
3429
|
-
}
|
|
3430
3516
|
logAdState(event, extra = {}) {
|
|
3431
3517
|
if (!this.config.debugAdTiming) {
|
|
3432
3518
|
return;
|
|
@@ -3441,22 +3527,6 @@ var StormcloudVideoPlayer = class {
|
|
|
3441
3527
|
...extra
|
|
3442
3528
|
});
|
|
3443
3529
|
}
|
|
3444
|
-
enforceAdHoldState() {
|
|
3445
|
-
this.video.dataset.stormcloudAdPlaying = "true";
|
|
3446
|
-
this.video.muted = true;
|
|
3447
|
-
this.video.volume = 0;
|
|
3448
|
-
console.log("[DEBUG-LAYER] \u{1F512} Enforced ad hold state (main video muted)");
|
|
3449
|
-
if (typeof this.ima.showPlaceholder === "function") {
|
|
3450
|
-
this.ima.showPlaceholder();
|
|
3451
|
-
}
|
|
3452
|
-
}
|
|
3453
|
-
releaseAdHoldState() {
|
|
3454
|
-
delete this.video.dataset.stormcloudAdPlaying;
|
|
3455
|
-
console.log("[DEBUG-LAYER] \u{1F513} Released ad hold state");
|
|
3456
|
-
if (typeof this.ima.hidePlaceholder === "function") {
|
|
3457
|
-
this.ima.hidePlaceholder();
|
|
3458
|
-
}
|
|
3459
|
-
}
|
|
3460
3530
|
async fetchAndParseVastXml(vastTagUrl) {
|
|
3461
3531
|
try {
|
|
3462
3532
|
const response = await fetch(vastTagUrl, { mode: "cors" });
|
|
@@ -3507,6 +3577,99 @@ var StormcloudVideoPlayer = class {
|
|
|
3507
3577
|
}
|
|
3508
3578
|
return mediaUrls;
|
|
3509
3579
|
}
|
|
3580
|
+
async fetchVastDuration(vastTagUrl) {
|
|
3581
|
+
var _a;
|
|
3582
|
+
try {
|
|
3583
|
+
const response = await fetch(vastTagUrl, { mode: "cors" });
|
|
3584
|
+
if (!response.ok) {
|
|
3585
|
+
if (this.config.debugAdTiming) {
|
|
3586
|
+
console.warn(
|
|
3587
|
+
`[ADAPTIVE-POD] Failed to fetch VAST: ${response.status}`
|
|
3588
|
+
);
|
|
3589
|
+
}
|
|
3590
|
+
return null;
|
|
3591
|
+
}
|
|
3592
|
+
const xmlText = await response.text();
|
|
3593
|
+
const parser = new DOMParser();
|
|
3594
|
+
const xmlDoc = parser.parseFromString(xmlText, "text/xml");
|
|
3595
|
+
const durationText = (_a = xmlDoc.querySelector("Duration")) == null ? void 0 : _a.textContent;
|
|
3596
|
+
if (!durationText) {
|
|
3597
|
+
if (this.config.debugAdTiming) {
|
|
3598
|
+
console.warn("[ADAPTIVE-POD] No Duration element found in VAST");
|
|
3599
|
+
}
|
|
3600
|
+
return null;
|
|
3601
|
+
}
|
|
3602
|
+
const durationParts = durationText.split(":");
|
|
3603
|
+
const durationSeconds = parseInt(durationParts[0] || "0", 10) * 3600 + parseInt(durationParts[1] || "0", 10) * 60 + parseInt(durationParts[2] || "0", 10);
|
|
3604
|
+
return durationSeconds;
|
|
3605
|
+
} catch (error) {
|
|
3606
|
+
if (this.config.debugAdTiming) {
|
|
3607
|
+
console.warn(
|
|
3608
|
+
`[ADAPTIVE-POD] Error fetching VAST duration from ${vastTagUrl}:`,
|
|
3609
|
+
error
|
|
3610
|
+
);
|
|
3611
|
+
}
|
|
3612
|
+
return null;
|
|
3613
|
+
}
|
|
3614
|
+
}
|
|
3615
|
+
calculateAdditionalAdsNeeded() {
|
|
3616
|
+
if (!this.isAdaptiveMode || this.targetAdBreakDurationMs === null) {
|
|
3617
|
+
return 0;
|
|
3618
|
+
}
|
|
3619
|
+
let totalFetchedDurationMs = 0;
|
|
3620
|
+
for (const duration of this.fetchedAdDurations.values()) {
|
|
3621
|
+
totalFetchedDurationMs += duration * 1e3;
|
|
3622
|
+
}
|
|
3623
|
+
const remainingTimeMs = this.targetAdBreakDurationMs - totalFetchedDurationMs;
|
|
3624
|
+
if (remainingTimeMs <= 0) {
|
|
3625
|
+
if (this.config.debugAdTiming) {
|
|
3626
|
+
console.log(
|
|
3627
|
+
`[ADAPTIVE-POD] \u2705 Target duration reached: ${totalFetchedDurationMs}ms / ${this.targetAdBreakDurationMs}ms`
|
|
3628
|
+
);
|
|
3629
|
+
}
|
|
3630
|
+
return 0;
|
|
3631
|
+
}
|
|
3632
|
+
const fetchedCount = this.fetchedAdDurations.size;
|
|
3633
|
+
const averageDurationMs = fetchedCount > 0 ? totalFetchedDurationMs / fetchedCount : 30 * 1e3;
|
|
3634
|
+
const additionalAds = Math.ceil(remainingTimeMs / averageDurationMs);
|
|
3635
|
+
if (this.config.debugAdTiming) {
|
|
3636
|
+
console.log(
|
|
3637
|
+
`[ADAPTIVE-POD] \u{1F4CA} Need ${additionalAds} more ads | Fetched: ${totalFetchedDurationMs}ms / Target: ${this.targetAdBreakDurationMs}ms | Remaining: ${remainingTimeMs}ms | Avg duration: ${averageDurationMs}ms`
|
|
3638
|
+
);
|
|
3639
|
+
}
|
|
3640
|
+
return additionalAds;
|
|
3641
|
+
}
|
|
3642
|
+
async addAdaptiveAdsToQueue() {
|
|
3643
|
+
if (!this.isAdaptiveMode || !this.apiVastTagUrl) {
|
|
3644
|
+
return;
|
|
3645
|
+
}
|
|
3646
|
+
const additionalAds = this.calculateAdditionalAdsNeeded();
|
|
3647
|
+
if (additionalAds <= 0) {
|
|
3648
|
+
return;
|
|
3649
|
+
}
|
|
3650
|
+
const newUrls = this.generateVastUrlsWithCorrelators(
|
|
3651
|
+
this.apiVastTagUrl,
|
|
3652
|
+
additionalAds
|
|
3653
|
+
);
|
|
3654
|
+
if (this.config.debugAdTiming) {
|
|
3655
|
+
console.log(
|
|
3656
|
+
`[ADAPTIVE-POD] \u{1F504} Adding ${newUrls.length} additional VAST URLs to queue`
|
|
3657
|
+
);
|
|
3658
|
+
}
|
|
3659
|
+
this.adPodAllUrls.push(...newUrls);
|
|
3660
|
+
this.adPodQueue.push(...newUrls);
|
|
3661
|
+
this.totalAdsInBreak += newUrls.length;
|
|
3662
|
+
for (const url of newUrls) {
|
|
3663
|
+
this.preloadSingleAd(url).catch((error) => {
|
|
3664
|
+
if (this.config.debugAdTiming) {
|
|
3665
|
+
console.warn(
|
|
3666
|
+
`[ADAPTIVE-POD] Failed to preload adaptive ad:`,
|
|
3667
|
+
error
|
|
3668
|
+
);
|
|
3669
|
+
}
|
|
3670
|
+
});
|
|
3671
|
+
}
|
|
3672
|
+
}
|
|
3510
3673
|
async preloadMediaFile(mediaUrl) {
|
|
3511
3674
|
if (this.preloadedMediaUrls.has(mediaUrl)) {
|
|
3512
3675
|
return;
|
|
@@ -3576,6 +3739,18 @@ var StormcloudVideoPlayer = class {
|
|
|
3576
3739
|
async preloadSingleAd(vastTagUrl) {
|
|
3577
3740
|
if (!vastTagUrl) return;
|
|
3578
3741
|
try {
|
|
3742
|
+
if (this.isAdaptiveMode && !this.fetchedAdDurations.has(vastTagUrl)) {
|
|
3743
|
+
const duration = await this.fetchVastDuration(vastTagUrl);
|
|
3744
|
+
if (duration !== null) {
|
|
3745
|
+
this.fetchedAdDurations.set(vastTagUrl, duration);
|
|
3746
|
+
if (this.config.debugAdTiming) {
|
|
3747
|
+
console.log(
|
|
3748
|
+
`[ADAPTIVE-POD] \u2713 Fetched ad duration: ${duration}s (${this.fetchedAdDurations.size} ads fetched so far)`
|
|
3749
|
+
);
|
|
3750
|
+
}
|
|
3751
|
+
await this.addAdaptiveAdsToQueue();
|
|
3752
|
+
}
|
|
3753
|
+
}
|
|
3579
3754
|
if (this.ima.preloadAds && !this.ima.hasPreloadedAd(vastTagUrl)) {
|
|
3580
3755
|
if (!this.preloadingAdUrls.has(vastTagUrl)) {
|
|
3581
3756
|
if (this.config.debugAdTiming) {
|
|
@@ -3821,7 +3996,6 @@ var StormcloudVideoPlayer = class {
|
|
|
3821
3996
|
}
|
|
3822
3997
|
(_a = this.hls) == null ? void 0 : _a.destroy();
|
|
3823
3998
|
(_b = this.ima) == null ? void 0 : _b.destroy();
|
|
3824
|
-
this.releaseAdHoldState();
|
|
3825
3999
|
this.preloadingAdUrls.clear();
|
|
3826
4000
|
this.vastToMediaUrlMap.clear();
|
|
3827
4001
|
this.preloadedMediaUrls.clear();
|