stormcloud-video-player 0.5.26 → 0.6.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/dist/stormcloud-vp.min.js +1 -1
- package/lib/index.cjs +660 -554
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +20 -17
- package/lib/index.d.ts +20 -17
- package/lib/index.js +653 -547
- package/lib/index.js.map +1 -1
- package/lib/player/StormcloudVideoPlayer.cjs +647 -541
- package/lib/player/StormcloudVideoPlayer.cjs.map +1 -1
- package/lib/player/StormcloudVideoPlayer.d.cts +2 -2
- package/lib/players/HlsPlayer.cjs +648 -542
- package/lib/players/HlsPlayer.cjs.map +1 -1
- package/lib/players/HlsPlayer.d.cts +1 -1
- package/lib/players/index.cjs +648 -542
- package/lib/players/index.cjs.map +1 -1
- package/lib/sdk/{prebidAdLayer.cjs → vastAdLayer.cjs} +264 -139
- package/lib/sdk/vastAdLayer.cjs.map +1 -0
- package/lib/sdk/{prebidAdLayer.d.cts → vastAdLayer.d.cts} +11 -9
- package/lib/sdk/{prebid.cjs → vastManager.cjs} +205 -171
- package/lib/sdk/vastManager.cjs.map +1 -0
- package/lib/sdk/vastManager.d.cts +8 -0
- package/lib/{types-BA1uZjfj.d.cts → types-CwDRIvJm.d.cts} +9 -8
- package/lib/ui/StormcloudVideoPlayer.cjs +650 -544
- package/lib/ui/StormcloudVideoPlayer.cjs.map +1 -1
- package/lib/ui/StormcloudVideoPlayer.d.cts +1 -1
- package/lib/utils/tracking.d.cts +1 -1
- package/package.json +1 -1
- package/lib/sdk/prebid.cjs.map +0 -1
- package/lib/sdk/prebid.d.cts +0 -8
- package/lib/sdk/prebidAdLayer.cjs.map +0 -1
|
@@ -223,17 +223,149 @@ var __toCommonJS = function __toCommonJS(mod) {
|
|
|
223
223
|
value: true
|
|
224
224
|
}), mod);
|
|
225
225
|
};
|
|
226
|
-
// src/sdk/
|
|
227
|
-
var
|
|
228
|
-
__export(
|
|
229
|
-
|
|
230
|
-
return
|
|
226
|
+
// src/sdk/vastManager.ts
|
|
227
|
+
var vastManager_exports = {};
|
|
228
|
+
__export(vastManager_exports, {
|
|
229
|
+
createVastManager: function createVastManager1() {
|
|
230
|
+
return createVastManager;
|
|
231
231
|
}
|
|
232
232
|
});
|
|
233
|
-
module.exports = __toCommonJS(
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
233
|
+
module.exports = __toCommonJS(vastManager_exports);
|
|
234
|
+
// src/sdk/vastParser.ts
|
|
235
|
+
function isHlsType(type) {
|
|
236
|
+
return type === "application/x-mpegURL" || type.includes("m3u8");
|
|
237
|
+
}
|
|
238
|
+
function isMp4Type(type) {
|
|
239
|
+
return type === "video/mp4" || type.includes("mp4");
|
|
240
|
+
}
|
|
241
|
+
function parseVastXml(xmlString) {
|
|
242
|
+
var filter = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "all", logPrefix = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : "[VastParser]";
|
|
243
|
+
try {
|
|
244
|
+
var _xmlDoc_querySelector, _xmlDoc_querySelector1, _xmlDoc_querySelector_textContent, _xmlDoc_querySelector2;
|
|
245
|
+
var parser = new DOMParser();
|
|
246
|
+
var xmlDoc = parser.parseFromString(xmlString, "text/xml");
|
|
247
|
+
var parserError = xmlDoc.querySelector("parsererror");
|
|
248
|
+
if (parserError) {
|
|
249
|
+
console.error("".concat(logPrefix, " XML parsing error (malformed VAST XML):"), parserError.textContent);
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
var adElement = xmlDoc.querySelector("Ad");
|
|
253
|
+
if (!adElement) {
|
|
254
|
+
console.warn("".concat(logPrefix, " No Ad element found in VAST XML"));
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
var adId = adElement.getAttribute("id") || "unknown";
|
|
258
|
+
var title = ((_xmlDoc_querySelector = xmlDoc.querySelector("AdTitle")) === null || _xmlDoc_querySelector === void 0 ? void 0 : _xmlDoc_querySelector.textContent) || "Ad";
|
|
259
|
+
var isNoAdAvailable = adId === "empty" || title.toLowerCase().includes("no ad available") || title.toLowerCase() === "no ad available";
|
|
260
|
+
var durationText = ((_xmlDoc_querySelector1 = xmlDoc.querySelector("Duration")) === null || _xmlDoc_querySelector1 === void 0 ? void 0 : _xmlDoc_querySelector1.textContent) || "00:00:30";
|
|
261
|
+
var durationParts = durationText.split(":");
|
|
262
|
+
var duration = parseInt(durationParts[0] || "0", 10) * 3600 + parseInt(durationParts[1] || "0", 10) * 60 + Math.round(parseFloat(durationParts[2] || "0"));
|
|
263
|
+
var mediaFileElements = xmlDoc.querySelectorAll("MediaFile");
|
|
264
|
+
var mediaFiles = [];
|
|
265
|
+
console.log("".concat(logPrefix, " Found ").concat(mediaFileElements.length, " MediaFile element(s) in VAST XML"));
|
|
266
|
+
mediaFileElements.forEach(function(mf, index) {
|
|
267
|
+
var _mf_textContent;
|
|
268
|
+
var type = mf.getAttribute("type") || "";
|
|
269
|
+
var url = ((_mf_textContent = mf.textContent) === null || _mf_textContent === void 0 ? void 0 : _mf_textContent.trim()) || "";
|
|
270
|
+
var width = mf.getAttribute("width") || "";
|
|
271
|
+
var height = mf.getAttribute("height") || "";
|
|
272
|
+
console.log("".concat(logPrefix, " MediaFile ").concat(index, ': type="').concat(type, '", url="').concat(url.substring(0, 80), '...", width="').concat(width, '", height="').concat(height, '"'));
|
|
273
|
+
if (!url) {
|
|
274
|
+
console.warn("".concat(logPrefix, " MediaFile ").concat(index, " has empty URL"));
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
var isHls = isHlsType(type);
|
|
278
|
+
var isMp4 = isMp4Type(type);
|
|
279
|
+
var accepted = false;
|
|
280
|
+
if (filter === "hls-only") {
|
|
281
|
+
accepted = isHls;
|
|
282
|
+
} else if (filter === "mp4-first") {
|
|
283
|
+
accepted = isMp4 || isHls;
|
|
284
|
+
} else {
|
|
285
|
+
accepted = true;
|
|
286
|
+
}
|
|
287
|
+
if (!accepted) {
|
|
288
|
+
console.log("".concat(logPrefix, " MediaFile ").concat(index, ' ignored (type="').concat(type, '" not accepted by filter "').concat(filter, '")'));
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
var bitrateAttr = mf.getAttribute("bitrate");
|
|
292
|
+
var bitrateValue = bitrateAttr ? parseInt(bitrateAttr, 10) : void 0;
|
|
293
|
+
mediaFiles.push({
|
|
294
|
+
url: url,
|
|
295
|
+
type: type,
|
|
296
|
+
width: parseInt(width || "1920", 10),
|
|
297
|
+
height: parseInt(height || "1080", 10),
|
|
298
|
+
bitrate: bitrateValue && bitrateValue > 0 ? bitrateValue : void 0
|
|
299
|
+
});
|
|
300
|
+
console.log("".concat(logPrefix, ' Added MediaFile: type="').concat(type, '" url="').concat(url.substring(0, 80), '..."'));
|
|
301
|
+
});
|
|
302
|
+
if (filter === "mp4-first" && mediaFiles.length > 1) {
|
|
303
|
+
mediaFiles.sort(function(a, b) {
|
|
304
|
+
var aIsMp4 = isMp4Type(a.type) ? 0 : 1;
|
|
305
|
+
var bIsMp4 = isMp4Type(b.type) ? 0 : 1;
|
|
306
|
+
return aIsMp4 - bIsMp4;
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
if (mediaFiles.length === 0) {
|
|
310
|
+
if (isNoAdAvailable) {
|
|
311
|
+
console.warn("".concat(logPrefix, " No ads available (VAST response indicates no ads)"));
|
|
312
|
+
} else {
|
|
313
|
+
console.warn("".concat(logPrefix, " No compatible media files found in VAST XML"));
|
|
314
|
+
}
|
|
315
|
+
return null;
|
|
316
|
+
}
|
|
317
|
+
var trackingUrls = {
|
|
318
|
+
impression: [],
|
|
319
|
+
start: [],
|
|
320
|
+
firstQuartile: [],
|
|
321
|
+
midpoint: [],
|
|
322
|
+
thirdQuartile: [],
|
|
323
|
+
complete: [],
|
|
324
|
+
mute: [],
|
|
325
|
+
unmute: [],
|
|
326
|
+
pause: [],
|
|
327
|
+
resume: [],
|
|
328
|
+
fullscreen: [],
|
|
329
|
+
exitFullscreen: [],
|
|
330
|
+
skip: [],
|
|
331
|
+
error: []
|
|
332
|
+
};
|
|
333
|
+
xmlDoc.querySelectorAll("Impression").forEach(function(el) {
|
|
334
|
+
var _el_textContent;
|
|
335
|
+
var url = (_el_textContent = el.textContent) === null || _el_textContent === void 0 ? void 0 : _el_textContent.trim();
|
|
336
|
+
if (url) trackingUrls.impression.push(url);
|
|
337
|
+
});
|
|
338
|
+
xmlDoc.querySelectorAll("Tracking").forEach(function(el) {
|
|
339
|
+
var _el_textContent;
|
|
340
|
+
var event = el.getAttribute("event");
|
|
341
|
+
var url = (_el_textContent = el.textContent) === null || _el_textContent === void 0 ? void 0 : _el_textContent.trim();
|
|
342
|
+
if (event && url) {
|
|
343
|
+
var eventKey = event;
|
|
344
|
+
if (trackingUrls[eventKey]) {
|
|
345
|
+
trackingUrls[eventKey].push(url);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
var clickThrough = (_xmlDoc_querySelector2 = xmlDoc.querySelector("ClickThrough")) === null || _xmlDoc_querySelector2 === void 0 ? void 0 : (_xmlDoc_querySelector_textContent = _xmlDoc_querySelector2.textContent) === null || _xmlDoc_querySelector_textContent === void 0 ? void 0 : _xmlDoc_querySelector_textContent.trim();
|
|
350
|
+
return {
|
|
351
|
+
id: adId,
|
|
352
|
+
title: title,
|
|
353
|
+
duration: duration,
|
|
354
|
+
mediaFiles: mediaFiles,
|
|
355
|
+
trackingUrls: trackingUrls,
|
|
356
|
+
clickThrough: clickThrough
|
|
357
|
+
};
|
|
358
|
+
} catch (error) {
|
|
359
|
+
console.error("".concat(logPrefix, " Error parsing VAST XML:"), error);
|
|
360
|
+
return null;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
// src/sdk/vastManager.ts
|
|
364
|
+
var VAST_TAG_URL = "https://pubads.g.doubleclick.net/gampad/ads?iu=/21821455290/Airy-Android&description_url=http%3A%2F%2Fairy.tv&tfcd=0&npa=0&sz=1x1%7C300x250%7C400x300%7C640x480&gdfp_req=1&unviewed_position_start=1&correlator=[placeholder]&vpos=preroll&output=vast&env=vp&vpmute=0&vpa=click";
|
|
365
|
+
var DEFAULT_TIMEOUT_MS = 5e3;
|
|
366
|
+
var MAX_RETRIES = 3;
|
|
367
|
+
var RETRY_BACKOFF_MS = 1500;
|
|
368
|
+
function createVastManager() {
|
|
237
369
|
var options = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
|
|
238
370
|
var _options_debug;
|
|
239
371
|
var initialized = false;
|
|
@@ -245,7 +377,7 @@ function createPrebidManager() {
|
|
|
245
377
|
if (debug) {
|
|
246
378
|
var _console;
|
|
247
379
|
(_console = console).log.apply(_console, [
|
|
248
|
-
"[
|
|
380
|
+
"[VastManager]"
|
|
249
381
|
].concat(_to_consumable_array(args)));
|
|
250
382
|
}
|
|
251
383
|
}
|
|
@@ -255,75 +387,9 @@ function createPrebidManager() {
|
|
|
255
387
|
}
|
|
256
388
|
var _console;
|
|
257
389
|
(_console = console).warn.apply(_console, [
|
|
258
|
-
"[
|
|
390
|
+
"[VastManager]"
|
|
259
391
|
].concat(_to_consumable_array(args)));
|
|
260
392
|
}
|
|
261
|
-
function parseResponse(data) {
|
|
262
|
-
var bids = [];
|
|
263
|
-
var seatbids = (data === null || data === void 0 ? void 0 : data.seatbid) || [];
|
|
264
|
-
var currency = (data === null || data === void 0 ? void 0 : data.cur) || "USD";
|
|
265
|
-
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
266
|
-
try {
|
|
267
|
-
for(var _iterator = seatbids[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
268
|
-
var seatbid = _step.value;
|
|
269
|
-
var seat = seatbid.seat || "unknown";
|
|
270
|
-
var bidArray = seatbid.bid || [];
|
|
271
|
-
var _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
|
|
272
|
-
try {
|
|
273
|
-
for(var _iterator1 = bidArray[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
|
|
274
|
-
var bid = _step1.value;
|
|
275
|
-
var _bid_ext_prebid_cache_vastXml, _bid_ext_prebid_cache, _bid_ext_prebid, _bid_ext;
|
|
276
|
-
var cacheUrl = (_bid_ext = bid.ext) === null || _bid_ext === void 0 ? void 0 : (_bid_ext_prebid = _bid_ext.prebid) === null || _bid_ext_prebid === void 0 ? void 0 : (_bid_ext_prebid_cache = _bid_ext_prebid.cache) === null || _bid_ext_prebid_cache === void 0 ? void 0 : (_bid_ext_prebid_cache_vastXml = _bid_ext_prebid_cache.vastXml) === null || _bid_ext_prebid_cache_vastXml === void 0 ? void 0 : _bid_ext_prebid_cache_vastXml.url;
|
|
277
|
-
var vastXml = bid.adm || void 0;
|
|
278
|
-
var bidResponse = {
|
|
279
|
-
bidder: seat,
|
|
280
|
-
cpm: bid.price || 0,
|
|
281
|
-
width: bid.w || 0,
|
|
282
|
-
height: bid.h || 0,
|
|
283
|
-
adId: bid.id || "",
|
|
284
|
-
impId: bid.impid || "",
|
|
285
|
-
creativeId: bid.crid || "",
|
|
286
|
-
currency: currency
|
|
287
|
-
};
|
|
288
|
-
if (cacheUrl) bidResponse.vastUrl = cacheUrl;
|
|
289
|
-
if (vastXml) bidResponse.vastXml = vastXml;
|
|
290
|
-
if (bid.adomain) bidResponse.adomain = bid.adomain;
|
|
291
|
-
bids.push(bidResponse);
|
|
292
|
-
}
|
|
293
|
-
} catch (err) {
|
|
294
|
-
_didIteratorError1 = true;
|
|
295
|
-
_iteratorError1 = err;
|
|
296
|
-
} finally{
|
|
297
|
-
try {
|
|
298
|
-
if (!_iteratorNormalCompletion1 && _iterator1.return != null) {
|
|
299
|
-
_iterator1.return();
|
|
300
|
-
}
|
|
301
|
-
} finally{
|
|
302
|
-
if (_didIteratorError1) {
|
|
303
|
-
throw _iteratorError1;
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
} catch (err) {
|
|
309
|
-
_didIteratorError = true;
|
|
310
|
-
_iteratorError = err;
|
|
311
|
-
} finally{
|
|
312
|
-
try {
|
|
313
|
-
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
314
|
-
_iterator.return();
|
|
315
|
-
}
|
|
316
|
-
} finally{
|
|
317
|
-
if (_didIteratorError) {
|
|
318
|
-
throw _iteratorError;
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
bids.sort(function(a, b) {
|
|
323
|
-
return b.cpm - a.cpm;
|
|
324
|
-
});
|
|
325
|
-
return bids;
|
|
326
|
-
}
|
|
327
393
|
function initialize() {
|
|
328
394
|
return _async_to_generator(function() {
|
|
329
395
|
return _ts_generator(this, function(_state) {
|
|
@@ -331,131 +397,103 @@ function createPrebidManager() {
|
|
|
331
397
|
2
|
|
332
398
|
];
|
|
333
399
|
initialized = true;
|
|
334
|
-
log("Initialized,
|
|
400
|
+
log("Initialized, VAST tag URL:", VAST_TAG_URL.split("?")[0]);
|
|
335
401
|
return [
|
|
336
402
|
2
|
|
337
403
|
];
|
|
338
404
|
});
|
|
339
405
|
})();
|
|
340
406
|
}
|
|
341
|
-
function requestBids(
|
|
407
|
+
function requestBids(_context) {
|
|
342
408
|
return _async_to_generator(function() {
|
|
343
|
-
var
|
|
409
|
+
var correlator, url, controller, timeoutId, _ref, _ref1, _vastAd_mediaFiles_, _vastAd_mediaFiles_1, fetchOptions, response, vastXml, vastAd, bid, error;
|
|
344
410
|
return _ts_generator(this, function(_state) {
|
|
345
411
|
switch(_state.label){
|
|
346
412
|
case 0:
|
|
347
413
|
if (!initialized) {
|
|
348
|
-
throw new Error("
|
|
414
|
+
throw new Error("VastManager not initialized. Call initialize() first.");
|
|
349
415
|
}
|
|
350
|
-
|
|
351
|
-
|
|
416
|
+
correlator = Math.floor(Math.random() * 1e12).toString();
|
|
417
|
+
url = VAST_TAG_URL.replace("[placeholder]", correlator);
|
|
418
|
+
log("Fetching VAST tag, correlator:", correlator);
|
|
352
419
|
controller = typeof AbortController !== "undefined" ? new AbortController() : null;
|
|
353
420
|
timeoutId = setTimeout(function() {
|
|
354
|
-
controller === null || controller === void 0 ? void 0 : controller.abort();
|
|
355
|
-
},
|
|
421
|
+
return controller === null || controller === void 0 ? void 0 : controller.abort();
|
|
422
|
+
}, DEFAULT_TIMEOUT_MS);
|
|
356
423
|
_state.label = 1;
|
|
357
424
|
case 1:
|
|
358
425
|
_state.trys.push([
|
|
359
426
|
1,
|
|
360
|
-
|
|
427
|
+
4,
|
|
361
428
|
,
|
|
362
|
-
|
|
429
|
+
5
|
|
363
430
|
]);
|
|
364
431
|
fetchOptions = {
|
|
365
|
-
method: "
|
|
432
|
+
method: "GET",
|
|
433
|
+
mode: "cors",
|
|
434
|
+
credentials: "omit",
|
|
435
|
+
headers: {
|
|
436
|
+
Accept: "application/xml, text/xml, */*"
|
|
437
|
+
},
|
|
438
|
+
referrerPolicy: "no-referrer-when-downgrade"
|
|
366
439
|
};
|
|
367
|
-
if (controller)
|
|
368
|
-
fetchOptions.signal = controller.signal;
|
|
369
|
-
}
|
|
370
|
-
if ((context === null || context === void 0 ? void 0 : context.remainingBreakSec) != null) {
|
|
371
|
-
body = {
|
|
372
|
-
imp: [
|
|
373
|
-
{
|
|
374
|
-
video: {
|
|
375
|
-
maxduration: Math.floor(context.remainingBreakSec)
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
]
|
|
379
|
-
};
|
|
380
|
-
fetchOptions.body = JSON.stringify(body);
|
|
381
|
-
fetchOptions.headers = {
|
|
382
|
-
"Content-Type": "application/json"
|
|
383
|
-
};
|
|
384
|
-
log("Sending context to auction: maxduration=", Math.floor(context.remainingBreakSec));
|
|
385
|
-
}
|
|
440
|
+
if (controller) fetchOptions.signal = controller.signal;
|
|
386
441
|
return [
|
|
387
442
|
4,
|
|
388
|
-
fetch(
|
|
443
|
+
fetch(url, fetchOptions)
|
|
389
444
|
];
|
|
390
445
|
case 2:
|
|
391
446
|
response = _state.sent();
|
|
392
447
|
clearTimeout(timeoutId);
|
|
393
|
-
if (
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
];
|
|
448
|
+
if (!response.ok) {
|
|
449
|
+
throw new Error("VAST request returned HTTP ".concat(response.status));
|
|
450
|
+
}
|
|
397
451
|
return [
|
|
398
452
|
4,
|
|
399
|
-
response.text()
|
|
400
|
-
return "";
|
|
401
|
-
})
|
|
453
|
+
response.text()
|
|
402
454
|
];
|
|
403
455
|
case 3:
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
if (debug && (data === null || data === void 0 ? void 0 : (_data_ext = data.ext) === null || _data_ext === void 0 ? void 0 : _data_ext.responsetimemillis)) {
|
|
414
|
-
log("Bidder response times:", data.ext.responsetimemillis);
|
|
415
|
-
}
|
|
416
|
-
if (debug && (data === null || data === void 0 ? void 0 : (_data_ext1 = data.ext) === null || _data_ext1 === void 0 ? void 0 : _data_ext1.errors)) {
|
|
417
|
-
warn("Auction errors:", data.ext.errors);
|
|
418
|
-
}
|
|
419
|
-
bids = parseResponse(data);
|
|
420
|
-
log("Received ".concat(bids.length, " bid(s)"));
|
|
421
|
-
if (debug) {
|
|
422
|
-
_iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
423
|
-
try {
|
|
424
|
-
for(_iterator = bids[Symbol.iterator](); !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
425
|
-
b = _step.value;
|
|
426
|
-
log(" ".concat(b.bidder, ": $").concat(b.cpm.toFixed(2), " ").concat(b.currency, " ").concat(b.width, "x").concat(b.height) + (b.vastUrl ? " [cached VAST]" : "") + (b.vastXml && !b.vastUrl ? " [VAST XML]" : ""));
|
|
427
|
-
}
|
|
428
|
-
} catch (err) {
|
|
429
|
-
_didIteratorError = true;
|
|
430
|
-
_iteratorError = err;
|
|
431
|
-
} finally{
|
|
432
|
-
try {
|
|
433
|
-
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
434
|
-
_iterator.return();
|
|
435
|
-
}
|
|
436
|
-
} finally{
|
|
437
|
-
if (_didIteratorError) {
|
|
438
|
-
throw _iteratorError;
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
}
|
|
456
|
+
vastXml = _state.sent();
|
|
457
|
+
log("VAST XML received, length:", vastXml.length);
|
|
458
|
+
vastAd = parseVastXml(vastXml, "mp4-first", "[VastManager]");
|
|
459
|
+
if (!vastAd) {
|
|
460
|
+
log("VAST parsed but no usable ad found");
|
|
461
|
+
return [
|
|
462
|
+
2,
|
|
463
|
+
[]
|
|
464
|
+
];
|
|
442
465
|
}
|
|
466
|
+
log("Ad parsed: id=".concat(vastAd.id, ", duration=").concat(vastAd.duration, "s, mediaFiles=").concat(vastAd.mediaFiles.length));
|
|
467
|
+
bid = {
|
|
468
|
+
bidder: "vast-direct",
|
|
469
|
+
cpm: 0,
|
|
470
|
+
vastXml: vastXml,
|
|
471
|
+
width: (_ref = (_vastAd_mediaFiles_ = vastAd.mediaFiles[0]) === null || _vastAd_mediaFiles_ === void 0 ? void 0 : _vastAd_mediaFiles_.width) !== null && _ref !== void 0 ? _ref : 0,
|
|
472
|
+
height: (_ref1 = (_vastAd_mediaFiles_1 = vastAd.mediaFiles[0]) === null || _vastAd_mediaFiles_1 === void 0 ? void 0 : _vastAd_mediaFiles_1.height) !== null && _ref1 !== void 0 ? _ref1 : 0,
|
|
473
|
+
adId: vastAd.id,
|
|
474
|
+
impId: correlator,
|
|
475
|
+
creativeId: vastAd.id,
|
|
476
|
+
currency: "USD",
|
|
477
|
+
durationSec: vastAd.duration
|
|
478
|
+
};
|
|
443
479
|
return [
|
|
444
480
|
2,
|
|
445
|
-
|
|
481
|
+
[
|
|
482
|
+
bid
|
|
483
|
+
]
|
|
446
484
|
];
|
|
447
|
-
case
|
|
485
|
+
case 4:
|
|
448
486
|
error = _state.sent();
|
|
449
487
|
clearTimeout(timeoutId);
|
|
450
488
|
if ((error === null || error === void 0 ? void 0 : error.name) === "AbortError") {
|
|
451
|
-
warn("
|
|
489
|
+
warn("VAST request timed out after ".concat(DEFAULT_TIMEOUT_MS, "ms"));
|
|
452
490
|
return [
|
|
453
491
|
2,
|
|
454
492
|
[]
|
|
455
493
|
];
|
|
456
494
|
}
|
|
457
495
|
throw error;
|
|
458
|
-
case
|
|
496
|
+
case 5:
|
|
459
497
|
return [
|
|
460
498
|
2
|
|
461
499
|
];
|
|
@@ -463,8 +501,6 @@ function createPrebidManager() {
|
|
|
463
501
|
});
|
|
464
502
|
})();
|
|
465
503
|
}
|
|
466
|
-
var REQUEST_BIDS_MAX_RETRIES = 3;
|
|
467
|
-
var REQUEST_BIDS_BACKOFF_MS = 1500;
|
|
468
504
|
function requestBidsUntilResponse(context) {
|
|
469
505
|
return _async_to_generator(function() {
|
|
470
506
|
var _loop, lastError, attempt, _ret;
|
|
@@ -489,7 +525,7 @@ function createPrebidManager() {
|
|
|
489
525
|
case 1:
|
|
490
526
|
bids = _state.sent();
|
|
491
527
|
if (bids.length > 0) {
|
|
492
|
-
log("requestBidsUntilResponse: got ".concat(bids.length, "
|
|
528
|
+
log("requestBidsUntilResponse: got ".concat(bids.length, " ad(s) on attempt ").concat(attempt));
|
|
493
529
|
return [
|
|
494
530
|
2,
|
|
495
531
|
{
|
|
@@ -497,7 +533,7 @@ function createPrebidManager() {
|
|
|
497
533
|
}
|
|
498
534
|
];
|
|
499
535
|
}
|
|
500
|
-
log("requestBidsUntilResponse: no
|
|
536
|
+
log("requestBidsUntilResponse: no ads on attempt ".concat(attempt, "/").concat(MAX_RETRIES));
|
|
501
537
|
return [
|
|
502
538
|
3,
|
|
503
539
|
3
|
|
@@ -505,17 +541,17 @@ function createPrebidManager() {
|
|
|
505
541
|
case 2:
|
|
506
542
|
err = _state.sent();
|
|
507
543
|
lastError = err;
|
|
508
|
-
warn("requestBidsUntilResponse: attempt ".concat(attempt, "/").concat(
|
|
544
|
+
warn("requestBidsUntilResponse: attempt ".concat(attempt, "/").concat(MAX_RETRIES, " failed:"), err);
|
|
509
545
|
return [
|
|
510
546
|
3,
|
|
511
547
|
3
|
|
512
548
|
];
|
|
513
549
|
case 3:
|
|
514
|
-
if (!(attempt <
|
|
550
|
+
if (!(attempt < MAX_RETRIES)) return [
|
|
515
551
|
3,
|
|
516
552
|
5
|
|
517
553
|
];
|
|
518
|
-
delay =
|
|
554
|
+
delay = RETRY_BACKOFF_MS * attempt;
|
|
519
555
|
log("requestBidsUntilResponse: waiting ".concat(delay, "ms before retry"));
|
|
520
556
|
return [
|
|
521
557
|
4,
|
|
@@ -534,12 +570,12 @@ function createPrebidManager() {
|
|
|
534
570
|
});
|
|
535
571
|
};
|
|
536
572
|
if (!initialized) {
|
|
537
|
-
throw new Error("
|
|
573
|
+
throw new Error("VastManager not initialized. Call initialize() first.");
|
|
538
574
|
}
|
|
539
575
|
attempt = 1;
|
|
540
576
|
_state.label = 1;
|
|
541
577
|
case 1:
|
|
542
|
-
if (!(attempt <=
|
|
578
|
+
if (!(attempt <= MAX_RETRIES)) return [
|
|
543
579
|
3,
|
|
544
580
|
4
|
|
545
581
|
];
|
|
@@ -561,9 +597,7 @@ function createPrebidManager() {
|
|
|
561
597
|
1
|
|
562
598
|
];
|
|
563
599
|
case 4:
|
|
564
|
-
if (_instanceof(lastError, Error))
|
|
565
|
-
throw lastError;
|
|
566
|
-
}
|
|
600
|
+
if (_instanceof(lastError, Error)) throw lastError;
|
|
567
601
|
return [
|
|
568
602
|
2,
|
|
569
603
|
[]
|
|
@@ -588,6 +622,6 @@ function createPrebidManager() {
|
|
|
588
622
|
}
|
|
589
623
|
// Annotate the CommonJS export names for ESM import in node:
|
|
590
624
|
0 && (module.exports = {
|
|
591
|
-
|
|
625
|
+
createVastManager: createVastManager
|
|
592
626
|
});
|
|
593
|
-
//# sourceMappingURL=
|
|
627
|
+
//# sourceMappingURL=vastManager.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/home/ubuntu24-new/Dev/stormcloud-vp/lib/sdk/vastManager.cjs","../../src/sdk/vastManager.ts","../../src/sdk/vastParser.ts"],"names":["__defProp","Object","defineProperty","__getOwnPropDesc","getOwnPropertyDescriptor","__getOwnPropNames","getOwnPropertyNames","__hasOwnProp","prototype","hasOwnProperty","__export","target","all","from","to","vastManager_exports","name","get","enumerable","__copyProps","except","desc","key","call","__toCommonJS","mod","value","createVastManager","module","exports","type","includes","parseVastXml","filter","xmlString","xmlDoc","parser","DOMParser","parseFromString","parserError","querySelector","console","error","logPrefix","textContent","adElement","warn","adId","getAttribute","isNoAdAvailable","title","toLowerCase","isMp4Type","durationText","durationParts","split","duration","parseInt","Math","round","parseFloat","mediaFileElements","querySelectorAll","mediaFiles","log","length","forEach","mf","index","url","trim","width","height","substring","isHls","isHlsType","isMp4","accepted"],"mappings":"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","sourcesContent":["\"use strict\";\nvar __defProp = Object.defineProperty;\nvar __getOwnPropDesc = Object.getOwnPropertyDescriptor;\nvar __getOwnPropNames = Object.getOwnPropertyNames;\nvar __hasOwnProp = Object.prototype.hasOwnProperty;\nvar __export = (target, all) => {\n for (var name in all)\n __defProp(target, name, { get: all[name], enumerable: true });\n};\nvar __copyProps = (to, from, except, desc) => {\n if (from && typeof from === \"object\" || typeof from === \"function\") {\n for (let key of __getOwnPropNames(from))\n if (!__hasOwnProp.call(to, key) && key !== except)\n __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });\n }\n return to;\n};\nvar __toCommonJS = (mod) => __copyProps(__defProp({}, \"__esModule\", { value: true }), mod);\n\n// src/sdk/vastManager.ts\nvar vastManager_exports = {};\n__export(vastManager_exports, {\n createVastManager: () => createVastManager\n});\nmodule.exports = __toCommonJS(vastManager_exports);\n\n// src/sdk/vastParser.ts\nfunction isHlsType(type) {\n return type === \"application/x-mpegURL\" || type.includes(\"m3u8\");\n}\nfunction isMp4Type(type) {\n return type === \"video/mp4\" || type.includes(\"mp4\");\n}\nfunction parseVastXml(xmlString, filter = \"all\", logPrefix = \"[VastParser]\") {\n try {\n const parser = new DOMParser();\n const xmlDoc = parser.parseFromString(xmlString, \"text/xml\");\n const parserError = xmlDoc.querySelector(\"parsererror\");\n if (parserError) {\n console.error(\n `${logPrefix} XML parsing error (malformed VAST XML):`,\n parserError.textContent\n );\n return null;\n }\n const adElement = xmlDoc.querySelector(\"Ad\");\n if (!adElement) {\n console.warn(`${logPrefix} No Ad element found in VAST XML`);\n return null;\n }\n const adId = adElement.getAttribute(\"id\") || \"unknown\";\n const title = xmlDoc.querySelector(\"AdTitle\")?.textContent || \"Ad\";\n const isNoAdAvailable = adId === \"empty\" || title.toLowerCase().includes(\"no ad available\") || title.toLowerCase() === \"no ad available\";\n const durationText = xmlDoc.querySelector(\"Duration\")?.textContent || \"00:00:30\";\n const durationParts = durationText.split(\":\");\n const duration = parseInt(durationParts[0] || \"0\", 10) * 3600 + parseInt(durationParts[1] || \"0\", 10) * 60 + Math.round(parseFloat(durationParts[2] || \"0\"));\n const mediaFileElements = xmlDoc.querySelectorAll(\"MediaFile\");\n const mediaFiles = [];\n console.log(\n `${logPrefix} Found ${mediaFileElements.length} MediaFile element(s) in VAST XML`\n );\n mediaFileElements.forEach((mf, index) => {\n const type = mf.getAttribute(\"type\") || \"\";\n const url = mf.textContent?.trim() || \"\";\n const width = mf.getAttribute(\"width\") || \"\";\n const height = mf.getAttribute(\"height\") || \"\";\n console.log(\n `${logPrefix} MediaFile ${index}: type=\"${type}\", url=\"${url.substring(0, 80)}...\", width=\"${width}\", height=\"${height}\"`\n );\n if (!url) {\n console.warn(`${logPrefix} MediaFile ${index} has empty URL`);\n return;\n }\n const isHls = isHlsType(type);\n const isMp4 = isMp4Type(type);\n let accepted = false;\n if (filter === \"hls-only\") {\n accepted = isHls;\n } else if (filter === \"mp4-first\") {\n accepted = isMp4 || isHls;\n } else {\n accepted = true;\n }\n if (!accepted) {\n console.log(\n `${logPrefix} MediaFile ${index} ignored (type=\"${type}\" not accepted by filter \"${filter}\")`\n );\n return;\n }\n const bitrateAttr = mf.getAttribute(\"bitrate\");\n const bitrateValue = bitrateAttr ? parseInt(bitrateAttr, 10) : void 0;\n mediaFiles.push({\n url,\n type,\n width: parseInt(width || \"1920\", 10),\n height: parseInt(height || \"1080\", 10),\n bitrate: bitrateValue && bitrateValue > 0 ? bitrateValue : void 0\n });\n console.log(`${logPrefix} Added MediaFile: type=\"${type}\" url=\"${url.substring(0, 80)}...\"`);\n });\n if (filter === \"mp4-first\" && mediaFiles.length > 1) {\n mediaFiles.sort((a, b) => {\n const aIsMp4 = isMp4Type(a.type) ? 0 : 1;\n const bIsMp4 = isMp4Type(b.type) ? 0 : 1;\n return aIsMp4 - bIsMp4;\n });\n }\n if (mediaFiles.length === 0) {\n if (isNoAdAvailable) {\n console.warn(\n `${logPrefix} No ads available (VAST response indicates no ads)`\n );\n } else {\n console.warn(`${logPrefix} No compatible media files found in VAST XML`);\n }\n return null;\n }\n const trackingUrls = {\n impression: [],\n start: [],\n firstQuartile: [],\n midpoint: [],\n thirdQuartile: [],\n complete: [],\n mute: [],\n unmute: [],\n pause: [],\n resume: [],\n fullscreen: [],\n exitFullscreen: [],\n skip: [],\n error: []\n };\n xmlDoc.querySelectorAll(\"Impression\").forEach((el) => {\n const url = el.textContent?.trim();\n if (url) trackingUrls.impression.push(url);\n });\n xmlDoc.querySelectorAll(\"Tracking\").forEach((el) => {\n const event = el.getAttribute(\"event\");\n const url = el.textContent?.trim();\n if (event && url) {\n const eventKey = event;\n if (trackingUrls[eventKey]) {\n trackingUrls[eventKey].push(url);\n }\n }\n });\n const clickThrough = xmlDoc.querySelector(\"ClickThrough\")?.textContent?.trim();\n return {\n id: adId,\n title,\n duration,\n mediaFiles,\n trackingUrls,\n clickThrough\n };\n } catch (error) {\n console.error(`${logPrefix} Error parsing VAST XML:`, error);\n return null;\n }\n}\n\n// src/sdk/vastManager.ts\nvar VAST_TAG_URL = \"https://pubads.g.doubleclick.net/gampad/ads?iu=/21821455290/Airy-Android&description_url=http%3A%2F%2Fairy.tv&tfcd=0&npa=0&sz=1x1%7C300x250%7C400x300%7C640x480&gdfp_req=1&unviewed_position_start=1&correlator=[placeholder]&vpos=preroll&output=vast&env=vp&vpmute=0&vpa=click\";\nvar DEFAULT_TIMEOUT_MS = 5e3;\nvar MAX_RETRIES = 3;\nvar RETRY_BACKOFF_MS = 1500;\nfunction createVastManager(options = {}) {\n let initialized = false;\n const debug = options.debug ?? false;\n function log(...args) {\n if (debug) {\n console.log(\"[VastManager]\", ...args);\n }\n }\n function warn(...args) {\n console.warn(\"[VastManager]\", ...args);\n }\n async function initialize() {\n if (initialized) return;\n initialized = true;\n log(\"Initialized, VAST tag URL:\", VAST_TAG_URL.split(\"?\")[0]);\n }\n async function requestBids(_context) {\n if (!initialized) {\n throw new Error(\"VastManager not initialized. Call initialize() first.\");\n }\n const correlator = Math.floor(Math.random() * 1e12).toString();\n const url = VAST_TAG_URL.replace(\"[placeholder]\", correlator);\n log(\"Fetching VAST tag, correlator:\", correlator);\n const controller = typeof AbortController !== \"undefined\" ? new AbortController() : null;\n const timeoutId = setTimeout(() => controller?.abort(), DEFAULT_TIMEOUT_MS);\n try {\n const fetchOptions = {\n method: \"GET\",\n mode: \"cors\",\n credentials: \"omit\",\n headers: { Accept: \"application/xml, text/xml, */*\" },\n referrerPolicy: \"no-referrer-when-downgrade\"\n };\n if (controller) fetchOptions.signal = controller.signal;\n const response = await fetch(url, fetchOptions);\n clearTimeout(timeoutId);\n if (!response.ok) {\n throw new Error(`VAST request returned HTTP ${response.status}`);\n }\n const vastXml = await response.text();\n log(\"VAST XML received, length:\", vastXml.length);\n const vastAd = parseVastXml(vastXml, \"mp4-first\", \"[VastManager]\");\n if (!vastAd) {\n log(\"VAST parsed but no usable ad found\");\n return [];\n }\n log(`Ad parsed: id=${vastAd.id}, duration=${vastAd.duration}s, mediaFiles=${vastAd.mediaFiles.length}`);\n const bid = {\n bidder: \"vast-direct\",\n cpm: 0,\n vastXml,\n width: vastAd.mediaFiles[0]?.width ?? 0,\n height: vastAd.mediaFiles[0]?.height ?? 0,\n adId: vastAd.id,\n impId: correlator,\n creativeId: vastAd.id,\n currency: \"USD\",\n durationSec: vastAd.duration\n };\n return [bid];\n } catch (error) {\n clearTimeout(timeoutId);\n if (error?.name === \"AbortError\") {\n warn(`VAST request timed out after ${DEFAULT_TIMEOUT_MS}ms`);\n return [];\n }\n throw error;\n }\n }\n async function requestBidsUntilResponse(context) {\n if (!initialized) {\n throw new Error(\"VastManager not initialized. Call initialize() first.\");\n }\n let lastError;\n for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {\n try {\n const bids = await requestBids(context);\n if (bids.length > 0) {\n log(`requestBidsUntilResponse: got ${bids.length} ad(s) on attempt ${attempt}`);\n return bids;\n }\n log(`requestBidsUntilResponse: no ads on attempt ${attempt}/${MAX_RETRIES}`);\n } catch (err) {\n lastError = err;\n warn(`requestBidsUntilResponse: attempt ${attempt}/${MAX_RETRIES} failed:`, err);\n }\n if (attempt < MAX_RETRIES) {\n const delay = RETRY_BACKOFF_MS * attempt;\n log(`requestBidsUntilResponse: waiting ${delay}ms before retry`);\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n if (lastError instanceof Error) throw lastError;\n return [];\n }\n function destroy() {\n initialized = false;\n log(\"Destroyed\");\n }\n return {\n initialize,\n requestBids,\n requestBidsUntilResponse,\n destroy,\n get isInitialized() {\n return initialized;\n }\n };\n}\n// Annotate the CommonJS export names for ESM import in node:\n0 && (module.exports = {\n createVastManager\n});\n","import { parseVastXml } from \"./vastParser\";\nimport type { VastBidResponse, VastManager, AdBreakContext } from \"../types\";\n\nconst VAST_TAG_URL =\n \"https://pubads.g.doubleclick.net/gampad/ads?iu=/21821455290/Airy-Android&description_url=http%3A%2F%2Fairy.tv&tfcd=0&npa=0&sz=1x1%7C300x250%7C400x300%7C640x480&gdfp_req=1&unviewed_position_start=1&correlator=[placeholder]&vpos=preroll&output=vast&env=vp&vpmute=0&vpa=click\";\n\nconst DEFAULT_TIMEOUT_MS = 5000;\nconst MAX_RETRIES = 3;\nconst RETRY_BACKOFF_MS = 1500;\n\nexport interface VastManagerOptions {\n debug?: boolean;\n}\n\nexport function createVastManager(\n options: VastManagerOptions = {}\n): VastManager {\n let initialized = false;\n const debug = options.debug ?? false;\n\n function log(...args: any[]): void {\n if (debug) {\n console.log(\"[VastManager]\", ...args);\n }\n }\n\n function warn(...args: any[]): void {\n console.warn(\"[VastManager]\", ...args);\n }\n\n async function initialize(): Promise<void> {\n if (initialized) return;\n initialized = true;\n log(\"Initialized, VAST tag URL:\", VAST_TAG_URL.split(\"?\")[0]);\n }\n\n async function requestBids(_context?: AdBreakContext): Promise<VastBidResponse[]> {\n if (!initialized) {\n throw new Error(\"VastManager not initialized. Call initialize() first.\");\n }\n\n const correlator = Math.floor(Math.random() * 1e12).toString();\n const url = VAST_TAG_URL.replace(\"[placeholder]\", correlator);\n\n log(\"Fetching VAST tag, correlator:\", correlator);\n\n const controller =\n typeof AbortController !== \"undefined\" ? new AbortController() : null;\n const timeoutId = setTimeout(() => controller?.abort(), DEFAULT_TIMEOUT_MS);\n\n try {\n const fetchOptions: RequestInit = {\n method: \"GET\",\n mode: \"cors\",\n credentials: \"omit\",\n headers: { Accept: \"application/xml, text/xml, */*\" },\n referrerPolicy: \"no-referrer-when-downgrade\",\n };\n if (controller) fetchOptions.signal = controller.signal;\n\n const response = await fetch(url, fetchOptions);\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n throw new Error(`VAST request returned HTTP ${response.status}`);\n }\n\n const vastXml = await response.text();\n log(\"VAST XML received, length:\", vastXml.length);\n\n const vastAd = parseVastXml(vastXml, \"mp4-first\", \"[VastManager]\");\n if (!vastAd) {\n log(\"VAST parsed but no usable ad found\");\n return [];\n }\n\n log(`Ad parsed: id=${vastAd.id}, duration=${vastAd.duration}s, mediaFiles=${vastAd.mediaFiles.length}`);\n\n const bid: VastBidResponse = {\n bidder: \"vast-direct\",\n cpm: 0,\n vastXml,\n width: vastAd.mediaFiles[0]?.width ?? 0,\n height: vastAd.mediaFiles[0]?.height ?? 0,\n adId: vastAd.id,\n impId: correlator,\n creativeId: vastAd.id,\n currency: \"USD\",\n durationSec: vastAd.duration,\n };\n\n return [bid];\n } catch (error: any) {\n clearTimeout(timeoutId);\n if (error?.name === \"AbortError\") {\n warn(`VAST request timed out after ${DEFAULT_TIMEOUT_MS}ms`);\n return [];\n }\n throw error;\n }\n }\n\n async function requestBidsUntilResponse(context?: AdBreakContext): Promise<VastBidResponse[]> {\n if (!initialized) {\n throw new Error(\"VastManager not initialized. Call initialize() first.\");\n }\n let lastError: unknown;\n for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {\n try {\n const bids = await requestBids(context);\n if (bids.length > 0) {\n log(`requestBidsUntilResponse: got ${bids.length} ad(s) on attempt ${attempt}`);\n return bids;\n }\n log(`requestBidsUntilResponse: no ads on attempt ${attempt}/${MAX_RETRIES}`);\n } catch (err) {\n lastError = err;\n warn(`requestBidsUntilResponse: attempt ${attempt}/${MAX_RETRIES} failed:`, err);\n }\n if (attempt < MAX_RETRIES) {\n const delay = RETRY_BACKOFF_MS * attempt;\n log(`requestBidsUntilResponse: waiting ${delay}ms before retry`);\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n if (lastError instanceof Error) throw lastError;\n return [];\n }\n\n function destroy(): void {\n initialized = false;\n log(\"Destroyed\");\n }\n\n return {\n initialize,\n requestBids,\n requestBidsUntilResponse,\n destroy,\n get isInitialized() {\n return initialized;\n },\n };\n}\n","export interface VastMediaFile {\n url: string;\n type: string;\n width: number;\n height: number;\n bitrate?: number | undefined;\n}\n\nexport interface VastTrackingUrls {\n impression: string[];\n start: string[];\n firstQuartile: string[];\n midpoint: string[];\n thirdQuartile: string[];\n complete: string[];\n mute: string[];\n unmute: string[];\n pause: string[];\n resume: string[];\n fullscreen: string[];\n exitFullscreen: string[];\n skip: string[];\n error: string[];\n}\n\nexport interface VastAd {\n id: string;\n title: string;\n duration: number;\n mediaFiles: VastMediaFile[];\n trackingUrls: VastTrackingUrls;\n clickThrough?: string | undefined;\n}\n\nexport type MediaFileFilter = \"hls-only\" | \"mp4-first\" | \"all\";\n\nfunction isHlsType(type: string): boolean {\n return type === \"application/x-mpegURL\" || type.includes(\"m3u8\");\n}\n\nfunction isMp4Type(type: string): boolean {\n return type === \"video/mp4\" || type.includes(\"mp4\");\n}\n\nexport function parseVastXml(\n xmlString: string,\n filter: MediaFileFilter = \"all\",\n logPrefix = \"[VastParser]\"\n): VastAd | null {\n try {\n const parser = new DOMParser();\n const xmlDoc = parser.parseFromString(xmlString, \"text/xml\");\n\n const parserError = xmlDoc.querySelector(\"parsererror\");\n if (parserError) {\n console.error(\n `${logPrefix} XML parsing error (malformed VAST XML):`,\n parserError.textContent\n );\n return null;\n }\n\n const adElement = xmlDoc.querySelector(\"Ad\");\n if (!adElement) {\n console.warn(`${logPrefix} No Ad element found in VAST XML`);\n return null;\n }\n\n const adId = adElement.getAttribute(\"id\") || \"unknown\";\n const title = xmlDoc.querySelector(\"AdTitle\")?.textContent || \"Ad\";\n\n const isNoAdAvailable =\n adId === \"empty\" ||\n title.toLowerCase().includes(\"no ad available\") ||\n title.toLowerCase() === \"no ad available\";\n\n const durationText =\n xmlDoc.querySelector(\"Duration\")?.textContent || \"00:00:30\";\n const durationParts = durationText.split(\":\");\n const duration =\n parseInt(durationParts[0] || \"0\", 10) * 3600 +\n parseInt(durationParts[1] || \"0\", 10) * 60 +\n Math.round(parseFloat(durationParts[2] || \"0\"));\n\n const mediaFileElements = xmlDoc.querySelectorAll(\"MediaFile\");\n const mediaFiles: VastMediaFile[] = [];\n\n console.log(\n `${logPrefix} Found ${mediaFileElements.length} MediaFile element(s) in VAST XML`\n );\n\n mediaFileElements.forEach((mf, index) => {\n const type = mf.getAttribute(\"type\") || \"\";\n const url = mf.textContent?.trim() || \"\";\n const width = mf.getAttribute(\"width\") || \"\";\n const height = mf.getAttribute(\"height\") || \"\";\n\n console.log(\n `${logPrefix} MediaFile ${index}: type=\"${type}\", url=\"${url.substring(0, 80)}...\", width=\"${width}\", height=\"${height}\"`\n );\n\n if (!url) {\n console.warn(`${logPrefix} MediaFile ${index} has empty URL`);\n return;\n }\n\n const isHls = isHlsType(type);\n const isMp4 = isMp4Type(type);\n\n let accepted = false;\n if (filter === \"hls-only\") {\n accepted = isHls;\n } else if (filter === \"mp4-first\") {\n accepted = isMp4 || isHls;\n } else {\n accepted = true;\n }\n\n if (!accepted) {\n console.log(\n `${logPrefix} MediaFile ${index} ignored (type=\"${type}\" not accepted by filter \"${filter}\")`\n );\n return;\n }\n\n const bitrateAttr = mf.getAttribute(\"bitrate\");\n const bitrateValue = bitrateAttr ? parseInt(bitrateAttr, 10) : undefined;\n\n mediaFiles.push({\n url,\n type,\n width: parseInt(width || \"1920\", 10),\n height: parseInt(height || \"1080\", 10),\n bitrate: bitrateValue && bitrateValue > 0 ? bitrateValue : undefined,\n });\n\n console.log(`${logPrefix} Added MediaFile: type=\"${type}\" url=\"${url.substring(0, 80)}...\"`);\n });\n\n if (filter === \"mp4-first\" && mediaFiles.length > 1) {\n mediaFiles.sort((a, b) => {\n const aIsMp4 = isMp4Type(a.type) ? 0 : 1;\n const bIsMp4 = isMp4Type(b.type) ? 0 : 1;\n return aIsMp4 - bIsMp4;\n });\n }\n\n if (mediaFiles.length === 0) {\n if (isNoAdAvailable) {\n console.warn(\n `${logPrefix} No ads available (VAST response indicates no ads)`\n );\n } else {\n console.warn(`${logPrefix} No compatible media files found in VAST XML`);\n }\n return null;\n }\n\n const trackingUrls: VastTrackingUrls = {\n impression: [],\n start: [],\n firstQuartile: [],\n midpoint: [],\n thirdQuartile: [],\n complete: [],\n mute: [],\n unmute: [],\n pause: [],\n resume: [],\n fullscreen: [],\n exitFullscreen: [],\n skip: [],\n error: [],\n };\n\n xmlDoc.querySelectorAll(\"Impression\").forEach((el) => {\n const url = el.textContent?.trim();\n if (url) trackingUrls.impression.push(url);\n });\n\n xmlDoc.querySelectorAll(\"Tracking\").forEach((el) => {\n const event = el.getAttribute(\"event\");\n const url = el.textContent?.trim();\n if (event && url) {\n const eventKey = event as keyof VastTrackingUrls;\n if (trackingUrls[eventKey]) {\n trackingUrls[eventKey].push(url);\n }\n }\n });\n\n const clickThrough = xmlDoc\n .querySelector(\"ClickThrough\")\n ?.textContent?.trim();\n\n return {\n id: adId,\n title,\n duration,\n mediaFiles,\n trackingUrls,\n clickThrough,\n };\n } catch (error) {\n console.error(`${logPrefix} Error parsing VAST XML:`, error);\n return null;\n }\n}\n\nexport async function fetchAndParseVastAd(\n vastTagUrl: string,\n filter: MediaFileFilter = \"all\",\n logPrefix = \"[VastParser]\"\n): Promise<VastAd | null> {\n const response = await fetch(vastTagUrl, {\n mode: \"cors\",\n credentials: \"include\",\n headers: {\n Accept: \"application/xml, text/xml, */*\",\n },\n referrerPolicy: \"no-referrer-when-downgrade\",\n });\n if (!response.ok) {\n throw new Error(`Failed to fetch VAST: ${response.statusText}`);\n }\n\n const vastXml = await response.text();\n console.log(`${logPrefix} VAST XML received`);\n console.log(\n `${logPrefix} VAST XML content (first 2000 chars):`,\n vastXml.substring(0, 2000)\n );\n\n return parseVastXml(vastXml, filter, logPrefix);\n}\n\nexport function createEmptyTrackingState() {\n return {\n impression: false,\n start: false,\n firstQuartile: false,\n midpoint: false,\n thirdQuartile: false,\n complete: false,\n };\n}\n\nasync function firePixelWithRetry(\n url: string,\n retries = 2,\n delayMs = 500,\n logPrefix = \"[VastParser]\"\n): Promise<void> {\n for (let attempt = 0; attempt <= retries; attempt++) {\n try {\n await fetch(url, {\n method: \"GET\",\n mode: \"no-cors\",\n cache: \"no-cache\",\n keepalive: true,\n });\n return;\n } catch {\n if (attempt < retries) {\n await new Promise((r) => setTimeout(r, delayMs * Math.pow(2, attempt)));\n } else {\n console.warn(`${logPrefix} Tracking pixel failed after ${retries + 1} attempts: ${url}`);\n }\n }\n }\n}\n\nexport function fireTrackingPixels(\n urls: string[],\n sessionId?: string,\n logPrefix = \"[VastParser]\"\n): void {\n if (!urls || urls.length === 0) return;\n\n urls.forEach((url) => {\n try {\n let trackingUrl = url;\n\n if (sessionId) {\n trackingUrl = `${trackingUrl}${\n trackingUrl.includes(\"?\") ? \"&\" : \"?\"\n }session_id=${sessionId}`;\n }\n\n if (typeof fetch !== \"undefined\") {\n firePixelWithRetry(trackingUrl, 2, 500, logPrefix).catch(() => {});\n } else {\n const img = new Image(1, 1);\n img.onerror = () => {};\n img.src = trackingUrl;\n }\n\n console.log(`${logPrefix} Fired tracking pixel: ${trackingUrl}`);\n } catch (error) {\n console.warn(`${logPrefix} Error firing tracking pixel:`, error);\n }\n });\n}\n"]}
|
|
@@ -18,7 +18,7 @@ interface StormcloudVideoPlayerConfig {
|
|
|
18
18
|
onControlClick?: () => void;
|
|
19
19
|
licenseKey?: string;
|
|
20
20
|
minSegmentsBeforePlay?: number;
|
|
21
|
-
|
|
21
|
+
disableAds?: boolean;
|
|
22
22
|
disableFiller?: boolean;
|
|
23
23
|
adTransitionGapMs?: number;
|
|
24
24
|
}
|
|
@@ -63,18 +63,18 @@ interface AdDetectInfo {
|
|
|
63
63
|
timestamp: string;
|
|
64
64
|
}
|
|
65
65
|
interface AdLoadedInfo {
|
|
66
|
-
source: "
|
|
66
|
+
source: "vast" | "ima" | "hls";
|
|
67
67
|
vastUrl?: string;
|
|
68
68
|
durationSeconds?: number;
|
|
69
69
|
timestamp: string;
|
|
70
70
|
}
|
|
71
71
|
interface AdImpressionInfo {
|
|
72
|
-
source: "
|
|
72
|
+
source: "vast" | "ima" | "hls";
|
|
73
73
|
adIndex: number;
|
|
74
74
|
durationSeconds?: number;
|
|
75
75
|
timestamp: string;
|
|
76
76
|
}
|
|
77
|
-
interface
|
|
77
|
+
interface VastBidResponse {
|
|
78
78
|
bidder: string;
|
|
79
79
|
cpm: number;
|
|
80
80
|
vastUrl?: string;
|
|
@@ -86,18 +86,19 @@ interface PrebidBidResponse {
|
|
|
86
86
|
creativeId: string;
|
|
87
87
|
currency: string;
|
|
88
88
|
adomain?: string[];
|
|
89
|
+
durationSec?: number;
|
|
89
90
|
}
|
|
90
91
|
interface AdBreakContext {
|
|
91
92
|
breakDurationSec?: number;
|
|
92
93
|
remainingBreakSec?: number;
|
|
93
94
|
adIndex?: number;
|
|
94
95
|
}
|
|
95
|
-
interface
|
|
96
|
+
interface VastManager {
|
|
96
97
|
initialize: () => Promise<void>;
|
|
97
|
-
requestBids: (context?: AdBreakContext) => Promise<
|
|
98
|
-
requestBidsUntilResponse: (context?: AdBreakContext) => Promise<
|
|
98
|
+
requestBids: (context?: AdBreakContext) => Promise<VastBidResponse[]>;
|
|
99
|
+
requestBidsUntilResponse: (context?: AdBreakContext) => Promise<VastBidResponse[]>;
|
|
99
100
|
destroy: () => void;
|
|
100
101
|
readonly isInitialized: boolean;
|
|
101
102
|
}
|
|
102
103
|
|
|
103
|
-
export type { AdDetectInfo as A, ClientInfo as C,
|
|
104
|
+
export type { AdDetectInfo as A, ClientInfo as C, StormcloudVideoPlayerConfig as S, VastBidResponse as V, VastManager as a, AdImpressionInfo as b, AdLoadedInfo as c };
|