stormcloud-video-player 0.6.10 → 0.6.11
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 +101 -13
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +2 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.js +101 -13
- package/lib/index.js.map +1 -1
- package/lib/player/StormcloudVideoPlayer.cjs +101 -13
- package/lib/player/StormcloudVideoPlayer.cjs.map +1 -1
- package/lib/players/HlsPlayer.cjs +101 -13
- package/lib/players/HlsPlayer.cjs.map +1 -1
- package/lib/players/index.cjs +101 -13
- package/lib/players/index.cjs.map +1 -1
- package/lib/sdk/vastManager.cjs +93 -10
- package/lib/sdk/vastManager.cjs.map +1 -1
- package/lib/sdk/vastManager.d.cts +2 -0
- package/lib/ui/StormcloudVideoPlayer.cjs +101 -13
- package/lib/ui/StormcloudVideoPlayer.cjs.map +1 -1
- package/package.json +1 -1
package/lib/sdk/vastManager.cjs
CHANGED
|
@@ -361,7 +361,8 @@ function parseVastXml(xmlString) {
|
|
|
361
361
|
}
|
|
362
362
|
}
|
|
363
363
|
// src/sdk/vastManager.ts
|
|
364
|
-
var
|
|
364
|
+
var VAST_TAG_URL_FALLBACK = "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 ADSTORM_API_URL = "https://adstorm.co/api-adstorm-dev/adstorm/ads/web";
|
|
365
366
|
var DEFAULT_TIMEOUT_MS = 5e3;
|
|
366
367
|
var MAX_RETRIES = 3;
|
|
367
368
|
var RETRY_BACKOFF_MS = 1500;
|
|
@@ -369,6 +370,7 @@ function createVastManager() {
|
|
|
369
370
|
var options = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
|
|
370
371
|
var _options_debug;
|
|
371
372
|
var initialized = false;
|
|
373
|
+
var vastTagUrl = VAST_TAG_URL_FALLBACK;
|
|
372
374
|
var debug = (_options_debug = options.debug) !== null && _options_debug !== void 0 ? _options_debug : false;
|
|
373
375
|
function log() {
|
|
374
376
|
for(var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++){
|
|
@@ -390,17 +392,97 @@ function createVastManager() {
|
|
|
390
392
|
"[VastManager]"
|
|
391
393
|
].concat(_to_consumable_array(args)));
|
|
392
394
|
}
|
|
395
|
+
function fetchVastUrlFromApi() {
|
|
396
|
+
return _async_to_generator(function() {
|
|
397
|
+
var apiUrl, _data_response_ima_publisherdeskima, _data_response_ima, _data_response, res, data, payload, err;
|
|
398
|
+
return _ts_generator(this, function(_state) {
|
|
399
|
+
switch(_state.label){
|
|
400
|
+
case 0:
|
|
401
|
+
apiUrl = options.adstormApiUrl || ADSTORM_API_URL;
|
|
402
|
+
_state.label = 1;
|
|
403
|
+
case 1:
|
|
404
|
+
_state.trys.push([
|
|
405
|
+
1,
|
|
406
|
+
4,
|
|
407
|
+
,
|
|
408
|
+
5
|
|
409
|
+
]);
|
|
410
|
+
return [
|
|
411
|
+
4,
|
|
412
|
+
fetch(apiUrl, {
|
|
413
|
+
method: "GET",
|
|
414
|
+
headers: {
|
|
415
|
+
Authorization: "Bearer ".concat(options.licenseKey),
|
|
416
|
+
Accept: "application/json"
|
|
417
|
+
}
|
|
418
|
+
})
|
|
419
|
+
];
|
|
420
|
+
case 2:
|
|
421
|
+
res = _state.sent();
|
|
422
|
+
if (!res.ok) {
|
|
423
|
+
warn("Failed to fetch VAST URL from API (HTTP ".concat(res.status, "), using fallback"));
|
|
424
|
+
return [
|
|
425
|
+
2
|
|
426
|
+
];
|
|
427
|
+
}
|
|
428
|
+
return [
|
|
429
|
+
4,
|
|
430
|
+
res.json()
|
|
431
|
+
];
|
|
432
|
+
case 3:
|
|
433
|
+
data = _state.sent();
|
|
434
|
+
payload = data === null || data === void 0 ? void 0 : (_data_response = data.response) === null || _data_response === void 0 ? void 0 : (_data_response_ima = _data_response.ima) === null || _data_response_ima === void 0 ? void 0 : (_data_response_ima_publisherdeskima = _data_response_ima["publisherdesk.ima"]) === null || _data_response_ima_publisherdeskima === void 0 ? void 0 : _data_response_ima_publisherdeskima.payload;
|
|
435
|
+
if (payload) {
|
|
436
|
+
vastTagUrl = payload;
|
|
437
|
+
log("VAST tag URL fetched from API:", vastTagUrl.split("?")[0]);
|
|
438
|
+
} else {
|
|
439
|
+
log("API response had no VAST payload, using fallback URL");
|
|
440
|
+
}
|
|
441
|
+
return [
|
|
442
|
+
3,
|
|
443
|
+
5
|
|
444
|
+
];
|
|
445
|
+
case 4:
|
|
446
|
+
err = _state.sent();
|
|
447
|
+
warn("Error fetching VAST URL from API, using fallback:", err);
|
|
448
|
+
return [
|
|
449
|
+
3,
|
|
450
|
+
5
|
|
451
|
+
];
|
|
452
|
+
case 5:
|
|
453
|
+
return [
|
|
454
|
+
2
|
|
455
|
+
];
|
|
456
|
+
}
|
|
457
|
+
});
|
|
458
|
+
})();
|
|
459
|
+
}
|
|
393
460
|
function initialize() {
|
|
394
461
|
return _async_to_generator(function() {
|
|
395
462
|
return _ts_generator(this, function(_state) {
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
463
|
+
switch(_state.label){
|
|
464
|
+
case 0:
|
|
465
|
+
if (initialized) return [
|
|
466
|
+
2
|
|
467
|
+
];
|
|
468
|
+
initialized = true;
|
|
469
|
+
if (!options.licenseKey) return [
|
|
470
|
+
3,
|
|
471
|
+
2
|
|
472
|
+
];
|
|
473
|
+
return [
|
|
474
|
+
4,
|
|
475
|
+
fetchVastUrlFromApi()
|
|
476
|
+
];
|
|
477
|
+
case 1:
|
|
478
|
+
_state.sent();
|
|
479
|
+
_state.label = 2;
|
|
480
|
+
case 2:
|
|
481
|
+
log("Initialized, VAST tag URL:", vastTagUrl.split("?")[0]);
|
|
482
|
+
return [
|
|
483
|
+
2
|
|
484
|
+
];
|
|
485
|
+
}
|
|
404
486
|
});
|
|
405
487
|
})();
|
|
406
488
|
}
|
|
@@ -414,7 +496,7 @@ function createVastManager() {
|
|
|
414
496
|
throw new Error("VastManager not initialized. Call initialize() first.");
|
|
415
497
|
}
|
|
416
498
|
correlator = Math.floor(Math.random() * 1e12).toString();
|
|
417
|
-
url =
|
|
499
|
+
url = vastTagUrl.replace("[placeholder]", correlator);
|
|
418
500
|
log("Fetching VAST tag, correlator:", correlator);
|
|
419
501
|
controller = typeof AbortController !== "undefined" ? new AbortController() : null;
|
|
420
502
|
timeoutId = setTimeout(function() {
|
|
@@ -608,6 +690,7 @@ function createVastManager() {
|
|
|
608
690
|
}
|
|
609
691
|
function destroy() {
|
|
610
692
|
initialized = false;
|
|
693
|
+
vastTagUrl = VAST_TAG_URL_FALLBACK;
|
|
611
694
|
log("Destroyed");
|
|
612
695
|
}
|
|
613
696
|
return {
|
|
@@ -1 +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"]}
|
|
1
|
+
{"version":3,"sources":["/home/ubuntu24-new/Dev/stormcloud-vp/lib/sdk/vastManager.cjs","../../src/sdk/vastParser.ts","../../src/sdk/vastManager.ts"],"names":["__defProp","Object","defineProperty","__getOwnPropDesc","getOwnPropertyDescriptor","__getOwnPropNames","__hasOwnProp","__export","from","getOwnPropertyNames","target","all","name","get","enumerable","__copyProps","to","except","desc","key","call","value","exports","__toCommonJS","isMp4Type","type","xmlDoc","isNoAdAvailable","adId","title","toLowerCase","includes","mod","vastManager_exports","createVastManager","isHlsType","parseVastXml","xmlString","filter","logPrefix","parser","DOMParser","parseFromString","parserError","querySelector","console","error","textContent","adElement","warn","getAttribute","durationText","durationParts","split","duration","parseInt","Math","round","parseFloat","mediaFileElements","querySelectorAll","mediaFiles","log","length","mf","index","url","width","substring","height","isHls","isMp4","accepted","trim","bitrateAttr","bitrateValue","push","bitrate","sort","a","b","aIsMp4","bIsMp4"],"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_FALLBACK = \"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 ADSTORM_API_URL = \"https://adstorm.co/api-adstorm-dev/adstorm/ads/web\";\nvar DEFAULT_TIMEOUT_MS = 5e3;\nvar MAX_RETRIES = 3;\nvar RETRY_BACKOFF_MS = 1500;\nfunction createVastManager(options = {}) {\n let initialized = false;\n let vastTagUrl = VAST_TAG_URL_FALLBACK;\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 fetchVastUrlFromApi() {\n const apiUrl = options.adstormApiUrl || ADSTORM_API_URL;\n try {\n const res = await fetch(apiUrl, {\n method: \"GET\",\n headers: {\n Authorization: `Bearer ${options.licenseKey}`,\n Accept: \"application/json\"\n }\n });\n if (!res.ok) {\n warn(`Failed to fetch VAST URL from API (HTTP ${res.status}), using fallback`);\n return;\n }\n const data = await res.json();\n const payload = data?.response?.ima?.[\"publisherdesk.ima\"]?.payload;\n if (payload) {\n vastTagUrl = payload;\n log(\"VAST tag URL fetched from API:\", vastTagUrl.split(\"?\")[0]);\n } else {\n log(\"API response had no VAST payload, using fallback URL\");\n }\n } catch (err) {\n warn(\"Error fetching VAST URL from API, using fallback:\", err);\n }\n }\n async function initialize() {\n if (initialized) return;\n initialized = true;\n if (options.licenseKey) {\n await fetchVastUrlFromApi();\n }\n log(\"Initialized, VAST tag URL:\", vastTagUrl.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 = vastTagUrl.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 vastTagUrl = VAST_TAG_URL_FALLBACK;\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","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","import { parseVastXml } from \"./vastParser\";\nimport type { VastBidResponse, VastManager, AdBreakContext, StormcloudApiResponse } from \"../types\";\n\nconst VAST_TAG_URL_FALLBACK =\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 ADSTORM_API_URL = \"https://adstorm.co/api-adstorm-dev/adstorm/ads/web\";\n\nconst DEFAULT_TIMEOUT_MS = 5000;\nconst MAX_RETRIES = 3;\nconst RETRY_BACKOFF_MS = 1500;\n\nexport interface VastManagerOptions {\n debug?: boolean;\n licenseKey?: string;\n adstormApiUrl?: string;\n}\n\nexport function createVastManager(\n options: VastManagerOptions = {}\n): VastManager {\n let initialized = false;\n let vastTagUrl = VAST_TAG_URL_FALLBACK;\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 fetchVastUrlFromApi(): Promise<void> {\n const apiUrl = options.adstormApiUrl || ADSTORM_API_URL;\n try {\n const res = await fetch(apiUrl, {\n method: \"GET\",\n headers: {\n Authorization: `Bearer ${options.licenseKey}`,\n Accept: \"application/json\",\n },\n });\n if (!res.ok) {\n warn(`Failed to fetch VAST URL from API (HTTP ${res.status}), using fallback`);\n return;\n }\n const data: StormcloudApiResponse = await res.json();\n const payload = data?.response?.ima?.[\"publisherdesk.ima\"]?.payload;\n if (payload) {\n vastTagUrl = payload;\n log(\"VAST tag URL fetched from API:\", vastTagUrl.split(\"?\")[0]);\n } else {\n log(\"API response had no VAST payload, using fallback URL\");\n }\n } catch (err) {\n warn(\"Error fetching VAST URL from API, using fallback:\", err);\n }\n }\n\n async function initialize(): Promise<void> {\n if (initialized) return;\n initialized = true;\n if (options.licenseKey) {\n await fetchVastUrlFromApi();\n }\n log(\"Initialized, VAST tag URL:\", vastTagUrl.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 = vastTagUrl.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 vastTagUrl = VAST_TAG_URL_FALLBACK;\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"]}
|
|
@@ -687,7 +687,8 @@ function fireTrackingPixels(urls, sessionId) {
|
|
|
687
687
|
});
|
|
688
688
|
}
|
|
689
689
|
// src/sdk/vastManager.ts
|
|
690
|
-
var
|
|
690
|
+
var VAST_TAG_URL_FALLBACK = "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";
|
|
691
|
+
var ADSTORM_API_URL = "https://adstorm.co/api-adstorm-dev/adstorm/ads/web";
|
|
691
692
|
var DEFAULT_TIMEOUT_MS = 5e3;
|
|
692
693
|
var MAX_RETRIES = 3;
|
|
693
694
|
var RETRY_BACKOFF_MS = 1500;
|
|
@@ -695,6 +696,7 @@ function createVastManager() {
|
|
|
695
696
|
var options = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
|
|
696
697
|
var _options_debug;
|
|
697
698
|
var initialized = false;
|
|
699
|
+
var vastTagUrl = VAST_TAG_URL_FALLBACK;
|
|
698
700
|
var debug = (_options_debug = options.debug) !== null && _options_debug !== void 0 ? _options_debug : false;
|
|
699
701
|
function log() {
|
|
700
702
|
for(var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++){
|
|
@@ -716,17 +718,97 @@ function createVastManager() {
|
|
|
716
718
|
"[VastManager]"
|
|
717
719
|
].concat(_to_consumable_array(args)));
|
|
718
720
|
}
|
|
721
|
+
function fetchVastUrlFromApi() {
|
|
722
|
+
return _async_to_generator(function() {
|
|
723
|
+
var apiUrl, _data_response_ima_publisherdeskima, _data_response_ima, _data_response, res, data, payload, err;
|
|
724
|
+
return _ts_generator(this, function(_state) {
|
|
725
|
+
switch(_state.label){
|
|
726
|
+
case 0:
|
|
727
|
+
apiUrl = options.adstormApiUrl || ADSTORM_API_URL;
|
|
728
|
+
_state.label = 1;
|
|
729
|
+
case 1:
|
|
730
|
+
_state.trys.push([
|
|
731
|
+
1,
|
|
732
|
+
4,
|
|
733
|
+
,
|
|
734
|
+
5
|
|
735
|
+
]);
|
|
736
|
+
return [
|
|
737
|
+
4,
|
|
738
|
+
fetch(apiUrl, {
|
|
739
|
+
method: "GET",
|
|
740
|
+
headers: {
|
|
741
|
+
Authorization: "Bearer ".concat(options.licenseKey),
|
|
742
|
+
Accept: "application/json"
|
|
743
|
+
}
|
|
744
|
+
})
|
|
745
|
+
];
|
|
746
|
+
case 2:
|
|
747
|
+
res = _state.sent();
|
|
748
|
+
if (!res.ok) {
|
|
749
|
+
warn("Failed to fetch VAST URL from API (HTTP ".concat(res.status, "), using fallback"));
|
|
750
|
+
return [
|
|
751
|
+
2
|
|
752
|
+
];
|
|
753
|
+
}
|
|
754
|
+
return [
|
|
755
|
+
4,
|
|
756
|
+
res.json()
|
|
757
|
+
];
|
|
758
|
+
case 3:
|
|
759
|
+
data = _state.sent();
|
|
760
|
+
payload = data === null || data === void 0 ? void 0 : (_data_response = data.response) === null || _data_response === void 0 ? void 0 : (_data_response_ima = _data_response.ima) === null || _data_response_ima === void 0 ? void 0 : (_data_response_ima_publisherdeskima = _data_response_ima["publisherdesk.ima"]) === null || _data_response_ima_publisherdeskima === void 0 ? void 0 : _data_response_ima_publisherdeskima.payload;
|
|
761
|
+
if (payload) {
|
|
762
|
+
vastTagUrl = payload;
|
|
763
|
+
log("VAST tag URL fetched from API:", vastTagUrl.split("?")[0]);
|
|
764
|
+
} else {
|
|
765
|
+
log("API response had no VAST payload, using fallback URL");
|
|
766
|
+
}
|
|
767
|
+
return [
|
|
768
|
+
3,
|
|
769
|
+
5
|
|
770
|
+
];
|
|
771
|
+
case 4:
|
|
772
|
+
err = _state.sent();
|
|
773
|
+
warn("Error fetching VAST URL from API, using fallback:", err);
|
|
774
|
+
return [
|
|
775
|
+
3,
|
|
776
|
+
5
|
|
777
|
+
];
|
|
778
|
+
case 5:
|
|
779
|
+
return [
|
|
780
|
+
2
|
|
781
|
+
];
|
|
782
|
+
}
|
|
783
|
+
});
|
|
784
|
+
})();
|
|
785
|
+
}
|
|
719
786
|
function initialize() {
|
|
720
787
|
return _async_to_generator(function() {
|
|
721
788
|
return _ts_generator(this, function(_state) {
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
789
|
+
switch(_state.label){
|
|
790
|
+
case 0:
|
|
791
|
+
if (initialized) return [
|
|
792
|
+
2
|
|
793
|
+
];
|
|
794
|
+
initialized = true;
|
|
795
|
+
if (!options.licenseKey) return [
|
|
796
|
+
3,
|
|
797
|
+
2
|
|
798
|
+
];
|
|
799
|
+
return [
|
|
800
|
+
4,
|
|
801
|
+
fetchVastUrlFromApi()
|
|
802
|
+
];
|
|
803
|
+
case 1:
|
|
804
|
+
_state.sent();
|
|
805
|
+
_state.label = 2;
|
|
806
|
+
case 2:
|
|
807
|
+
log("Initialized, VAST tag URL:", vastTagUrl.split("?")[0]);
|
|
808
|
+
return [
|
|
809
|
+
2
|
|
810
|
+
];
|
|
811
|
+
}
|
|
730
812
|
});
|
|
731
813
|
})();
|
|
732
814
|
}
|
|
@@ -740,7 +822,7 @@ function createVastManager() {
|
|
|
740
822
|
throw new Error("VastManager not initialized. Call initialize() first.");
|
|
741
823
|
}
|
|
742
824
|
correlator = Math.floor(Math.random() * 1e12).toString();
|
|
743
|
-
url =
|
|
825
|
+
url = vastTagUrl.replace("[placeholder]", correlator);
|
|
744
826
|
log("Fetching VAST tag, correlator:", correlator);
|
|
745
827
|
controller = typeof AbortController !== "undefined" ? new AbortController() : null;
|
|
746
828
|
timeoutId = setTimeout(function() {
|
|
@@ -934,6 +1016,7 @@ function createVastManager() {
|
|
|
934
1016
|
}
|
|
935
1017
|
function destroy() {
|
|
936
1018
|
initialized = false;
|
|
1019
|
+
vastTagUrl = VAST_TAG_URL_FALLBACK;
|
|
937
1020
|
log("Destroyed");
|
|
938
1021
|
}
|
|
939
1022
|
return {
|
|
@@ -3121,9 +3204,11 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
|
|
|
3121
3204
|
this.video = config.videoElement;
|
|
3122
3205
|
this.adTransitionGapMs = (_this_config_adTransitionGapMs = this.config.adTransitionGapMs) !== null && _this_config_adTransitionGapMs !== void 0 ? _this_config_adTransitionGapMs : 100;
|
|
3123
3206
|
logBrowserInfo(config.debugAdTiming);
|
|
3124
|
-
this.vastManager = createVastManager(
|
|
3125
|
-
debug: !!config.debugAdTiming
|
|
3126
|
-
}
|
|
3207
|
+
this.vastManager = createVastManager(_object_spread({
|
|
3208
|
+
debug: config.debugAdTiming !== void 0 ? !!config.debugAdTiming : false
|
|
3209
|
+
}, config.licenseKey ? {
|
|
3210
|
+
licenseKey: config.licenseKey
|
|
3211
|
+
} : {}));
|
|
3127
3212
|
var browserForAdLayer = detectBrowser();
|
|
3128
3213
|
var isSinglePipeline = browserForAdLayer.isSmartTV || !!this.config.singlePipelineMode;
|
|
3129
3214
|
this.adLayer = createVastAdLayer(this.video, {
|
|
@@ -3174,6 +3259,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
|
|
|
3174
3259
|
this.attach();
|
|
3175
3260
|
}
|
|
3176
3261
|
this.initializeTracking();
|
|
3262
|
+
if (!this.config.disableAds) {
|
|
3263
|
+
this.vastManager.initialize().catch(function() {});
|
|
3264
|
+
}
|
|
3177
3265
|
if (!this.shouldUseNativeHls()) return [
|
|
3178
3266
|
3,
|
|
3179
3267
|
3
|