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.
@@ -223,17 +223,149 @@ var __toCommonJS = function __toCommonJS(mod) {
223
223
  value: true
224
224
  }), mod);
225
225
  };
226
- // src/sdk/prebid.ts
227
- var prebid_exports = {};
228
- __export(prebid_exports, {
229
- createPrebidManager: function createPrebidManager1() {
230
- return createPrebidManager;
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(prebid_exports);
234
- var DEFAULT_TIMEOUT_MS = 3e3;
235
- var AUCTION_URL = "https://sspproxy.adstorm.co/openrtb2/auction/adstorm";
236
- function createPrebidManager() {
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
- "[Prebid]"
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
- "[Prebid]"
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, auction URL:", AUCTION_URL);
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(context) {
407
+ function requestBids(_context) {
342
408
  return _async_to_generator(function() {
343
- var timeout, controller, timeoutId, _data_ext, _data_ext1, fetchOptions, body, response, body1, data, bids, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, b, error;
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("Prebid not initialized. Call initialize() first.");
414
+ throw new Error("VastManager not initialized. Call initialize() first.");
349
415
  }
350
- timeout = DEFAULT_TIMEOUT_MS;
351
- log("Fetching auction response from:", AUCTION_URL);
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
- }, timeout + 2e3);
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
- 6,
427
+ 4,
361
428
  ,
362
- 7
429
+ 5
363
430
  ]);
364
431
  fetchOptions = {
365
- method: "POST"
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(AUCTION_URL, fetchOptions)
443
+ fetch(url, fetchOptions)
389
444
  ];
390
445
  case 2:
391
446
  response = _state.sent();
392
447
  clearTimeout(timeoutId);
393
- if (!!response.ok) return [
394
- 3,
395
- 4
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().catch(function() {
400
- return "";
401
- })
453
+ response.text()
402
454
  ];
403
455
  case 3:
404
- body1 = _state.sent();
405
- throw new Error("Prebid Server returned HTTP ".concat(response.status, ": ").concat(body1.slice(0, 200)));
406
- case 4:
407
- return [
408
- 4,
409
- response.json()
410
- ];
411
- case 5:
412
- data = _state.sent();
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
- bids
481
+ [
482
+ bid
483
+ ]
446
484
  ];
447
- case 6:
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("Auction request timed out after ".concat(timeout + 2e3, "ms"));
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 7:
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, " bid(s) on attempt ").concat(attempt));
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 bids on attempt ".concat(attempt, "/").concat(REQUEST_BIDS_MAX_RETRIES));
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(REQUEST_BIDS_MAX_RETRIES, " failed:"), err);
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 < REQUEST_BIDS_MAX_RETRIES)) return [
550
+ if (!(attempt < MAX_RETRIES)) return [
515
551
  3,
516
552
  5
517
553
  ];
518
- delay = REQUEST_BIDS_BACKOFF_MS * attempt;
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("Prebid not initialized. Call initialize() first.");
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 <= REQUEST_BIDS_MAX_RETRIES)) return [
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
- createPrebidManager: createPrebidManager
625
+ createVastManager: createVastManager
592
626
  });
593
- //# sourceMappingURL=prebid.cjs.map
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":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gBACIA,IAAAA,QAAYC,KAAAA,EAAOC,cAAc;;;gBACjCC,cAAAA,KAAmBF,OAAOG,wBAAwB;gBAClDC,IAAAA,gBAAoBJ,OAAOK,OAAAA,YAAmB,CAAA,KAAA,CAAA,IAAA,CAAA,EAAA;;;;;QAClD,EAAIC,eAAeN,OAAOO,SAAS,CAACC,cAAc;;IAClD,EAAIC,OAAW,IAAA,QAAA,IAACC,IAAAA,IAAQC;;gBAEuC,YAC/D,KAEMC,MAAQ,CAAA,OACL,sBAIAC,qBACT,oEChBAC;;;;wBDME,IAAK,CAAA,GAAIC,QAAQJ,EAAAA,EACfZ,UAAUW,QAAQK,MAAM;8BAAEC,IAAAA,CAAKL,GAAG,CAACI,KAAK;4BAAEE,YAAY;wBAAK,aAAA,KAAA,KAAA,CAAA,KAAA,MAAA,KAAA,MAAA,QAAA;wBAC/D,MAAA,aAAA,OAAA,CAAA,iBAAA;wBACIC,IAAAA,UAAc,qBAACL,GAAAA,CAAID,MAAMO,QAAQC;wBAC/BR,aAAQ,CAAOA,MAAAA,oBAAAA,WAAP,GAAA,IAAA,EAAOA,KAAG,MAAM,OAAA,KAAY,OAAOA,SAAS,YAAY;oCAC7D,WAAA;mCAAA,uBAAA,iCAAA,OAAA,IAAA,KAAA;2BAAA,aAAA;;;;;;;;;;oCAAA,IAAIS,MAAJ;oCACH,IAAI,CAACf,aAAagB,IAAI,CAACT,IAAIQ,QAAQA,QAAQF,QACzCpB,UAAUc,IAAIQ,KAAK;wCAAEL,CAAAA,IAAK,SAALA;;4CAAWJ,IAAI,CAACS,IAAI;4BAAA;;sCAAEJ,YAAY,CAAEG,CAAAA,OAAOlB,iBAAiBU,MAAMS,IAAG,KAAMD,KAAKH,UAAU;kCAAC,MAAA,aAAA,MAAA,GAAA,WAAA,MAAA;;;;;;;8BAFpH,OAAA,CAAK,YAAWb,kBAAkBQ,0BAA7B,SAAA,6BAAA,QAAA,yBAAA;;gCAAA,EAAA,IAAA,MAAA,8BAAA,OAAA,SAAA,MAAA;8BAAA;;;;;;;;iCAAA,aAAA,SAAA,OAAA,MAAA;sCAAA;;;;;;sCAAA;2CAAA,EAAA,OAAA,OAAA,EAAA,EAAA,eAAA,OAAA,OAAA,QAAA,EAAA,kBAAA,OAAA,OAAA,UAAA,CAAA,MAAA;;;;4BAGP,SAAA;4BACA,GAAOC,EAAAA,WAAAA,sBAAAA,OAAAA,UAAAA,CAAAA,EAAAA,cAAAA,0CAAAA,oBAAAA,KAAAA,uCAAAA;4BACT,MAAA,YAAA,uBAAA,OAAA,UAAA,CAAA,EAAA,cAAA,2CAAA,qBAAA,MAAA,yCAAA;4BACIU,MAAAA,KAAe,EAAA,EAAA,kBAACC;+BAAQN,IAAAA,QAAYnB,UAAU,CAAC,GAAG,cAAc;4BAAE0B,OAAO,KAAA,OAAA,EAAA;4BAASD,UAAAA;;wBAEtF,mBAAyB;wBCnBzBV;;;gCAAAA,YAAA,CAAA;;;;wBAAAA,iBAAA;wBAAAY,aAAAA,IAAA,SAAAA;gFAAAA,CAAAA,IAAAA,MAAAA,cAAAA;;4BAAA;;;;wBAAAC,CAAAC,OAAA,GAAAL,aAAAT;wBD0BA,MAAA,YAAwB;;;;;;;UEWtB,OAAOe,SAAS,2BAA2BA,KAAKC,QAAA,CAAS;;IAC3D,SAAA,yBAAA,OAAA;;uBAMO,CAASC,UAEdC,GADAC,SAAA;;;;;gCAwBgBC,MAVZ,IAAO,CAMP,KAAO;;;;;;;;;;wCAIKA;;4CAAAA,QAQZA,IAAAA,oBAkHmBA,mCAAAA;;;wCA1HPA,OAAAA;wCAnBd,IAAMC,KAAAA,IAAS,EAAA,EAAIC,CAAAA,GAAAA;4CACnB,EAAMF,EAAAA,MAASC,OAAOE,eAAA,CAAgBJ,IAAW,OAAXA,KAAAA,GAAW,GAAA,EAAA,sBAAA,OAAA;4CAEjD;;gDAAA,EAAMK;uDAAAA;gDAAAA,KAAcJ,OAAOK,aAAA,CAAc;;wCACzC,IAAID,aAAa;4CACfE,OAAQC,KAAA,CACN,GAAY,OAATC,WAAS,aAAA,OAAA,SAAA,KAAA,OAAA,GACZJ,YAAYK,WAAA;;;;;;wCAEd;wCACF,YAAA;wCAEA,IAAMC,CAAAA,UAAYV,OAAOK,aAAA,CAAc,MAAA,OAAA,SAAA,KAAA,OAAA,aAAA,aAAA;;;;;;wDAE7BM,IAAA,CAAK,GAAY,GAAA,KAAzBL,AAAgBE,WAAS;;;;wCACzB,QAAO,mBAAA;wCACT,IAAA,qCAAA,OAAA,OAAA;;;4CAEMI,IAAAA,CAAOF,OAAAA,SAAAA,EAAUG;uDAAAA,EAAA,CAAa,QAAA,CAAS,QAAA;;;;wCAA7C,IAAMD;;;;;;;;4BAGN,IAAME,kBACJF,SAAS,WACTG,MAAMC,WAAA,GAAcpB,QAAA,CAAS,sBAC7BmB,MAAMC,WAAA,OAAkB;wBAlC9B,IAAA,CAASC,UAAUtB,GAAAA,CAAA;4BACjB,KAAOA,CAAAA,IAAAA,IAAS,EAAA,aAAeA,KAAKC,QAAA,CAAS;wBAC/C;wBAIEE,IAAAA,MAAAA;;;6BAAAA,CAAAA,WAAAA,WAAAA;;;;;;;;;;;;;;;;wBAAAA,gCAA0B,OAC1BU,YAAAA,iEAAY;;;;;;4BA6BJU,UACJlB,EADF,IAAMkB,OACJlB,QAAAA,MAAAA,YAAAA,OAAOK,aAAA,CAAc,yBAArBL,6CAAAA,uBAAkCS,WAAA,KAAe;4BACnD,IAAMU,gBAAgBD,aAAaE,KAAA,CAAM;;;;;;cACzC,IAAMC,WACJC,SAASH,aAAA,CAAc,EAAC,IAAK,KAAK,MAAM,OACxCG,SAASH,aAAA,CAAc,EAAC,IAAK,KAAK,MAAM,KACxCI,KAAKC,KAAA,CAAMC,WAAWN,aAAA,CAAc,EAAC,IAAK;;UAE5C,GAAA,CAAMO,oBAAoB1B,OAAO2B,gBAAA,CAAiB;YAClD,IAAMC,MAAAA,OAA8B,EAAC;YAErCtB,QAAQuB,GAAA,CACN,GAAsBH,OAAnBlB,WAAS,WAAkC,OAAxBkB,kBAAkBI,MAAM,EAAA;UAGhDJ,kBAAkBK,OAAA,CAAQ,SAACC,IAAIC;kBAEjBD;4BADZ,IAAMrC,OAAOqC,GAAGnB,YAAA,CAAa,WAAW;6BACxC,IAAMqB,MAAMF,EAAAA,kBAAAA,GAAGvB,WAAA,cAAHuB,sCAAAA,gBAAgBG,IAAA,OAAU;0CACtC,IAAMC,QAAQJ,GAAGnB,YAAA,CAAa,YAAY;yBAC1C,IAAMwB,SAASL,GAAGnB,YAAA,CAAa,aAAa;gBAE5CP,QAAQuB,GAAA,EACN,GAA0BI,OAAvBzB,WAAS,eAA8Bb,OAAhBsC,OAAK,YAA0BC,OAAfvC,MAAI,YAA+CyC,OAApCF,IAAII,SAAA,CAAU,GAAG,KAAG,iBAAmCD,OAAnBD,OAAK,eAAoB,OAANC,QAAM;kBAGxH,CAAA,GAAI,CAACH,KAAK;oBACR5B,QAAQK,IAAA,CAAK,GAA0BsB,OAAvBzB,WAAS,eAAmB,OAALyB,OAAK;kBAC5C;YACF;YAEA,IAAMM,QAAQC,UAAU7C,2BAAAA;YACxB,CAAA,GAAM8C,IAAAA,GAAAA,CAAQxB,UAAUtB;iCAExB,IAAI+C,WAAW;aACf,IAAI5C,WAAW,YAAY","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"]}
@@ -0,0 +1,8 @@
1
+ import { a as VastManager } from '../types-CwDRIvJm.cjs';
2
+
3
+ interface VastManagerOptions {
4
+ debug?: boolean;
5
+ }
6
+ declare function createVastManager(options?: VastManagerOptions): VastManager;
7
+
8
+ export { type VastManagerOptions, createVastManager };
@@ -18,7 +18,7 @@ interface StormcloudVideoPlayerConfig {
18
18
  onControlClick?: () => void;
19
19
  licenseKey?: string;
20
20
  minSegmentsBeforePlay?: number;
21
- disablePrebid?: boolean;
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: "prebid" | "ima" | "hls";
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: "prebid" | "ima" | "hls";
72
+ source: "vast" | "ima" | "hls";
73
73
  adIndex: number;
74
74
  durationSeconds?: number;
75
75
  timestamp: string;
76
76
  }
77
- interface PrebidBidResponse {
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 PrebidManager {
96
+ interface VastManager {
96
97
  initialize: () => Promise<void>;
97
- requestBids: (context?: AdBreakContext) => Promise<PrebidBidResponse[]>;
98
- requestBidsUntilResponse: (context?: AdBreakContext) => Promise<PrebidBidResponse[]>;
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, PrebidManager as P, StormcloudVideoPlayerConfig as S, PrebidBidResponse as a, AdImpressionInfo as b, AdLoadedInfo 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 };