stormcloud-video-player 0.8.69 → 0.8.70

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.
@@ -870,6 +870,40 @@ function applyFloorToVastUrl(url, floor) {
870
870
  reason: void 0
871
871
  };
872
872
  }
873
+ // src/player/EdgePodRequest.ts
874
+ var EDGE_POD_MAX_ADS = 10;
875
+ var EDGE_POD_EXCEEDS_BUDGET = "edge_pod_exceeds_budget";
876
+ function buildEdgePodUrl(baseUrl, remainingMs, remainingAdLimit) {
877
+ var url = new URL(baseUrl);
878
+ var maxMs = Math.max(0, Math.floor(remainingMs));
879
+ var maxAds = Math.max(1, Math.min(EDGE_POD_MAX_ADS, Math.floor(remainingAdLimit)));
880
+ url.searchParams.set("pmxd", String(maxMs));
881
+ url.searchParams.set("pmad", String(maxAds));
882
+ url.searchParams.set("duration", String(Math.floor(maxMs / 1e3)));
883
+ url.searchParams.set("count", String(maxAds));
884
+ return url.toString();
885
+ }
886
+ function classifyEdgeVastResponse(result) {
887
+ var _ref;
888
+ var _result_error;
889
+ if (result.error == null) {
890
+ return result.podAdCount > 0 ? "fill" : "no_fill";
891
+ }
892
+ var message = (_ref = (_result_error = result.error) === null || _result_error === void 0 ? void 0 : _result_error.message) !== null && _ref !== void 0 ? _ref : String(result.error);
893
+ if (/no ads available|fill rejected|no media file|no ad loaded/i.test(message) || message.includes(EDGE_POD_EXCEEDS_BUDGET)) {
894
+ return "no_fill";
895
+ }
896
+ return "invalid";
897
+ }
898
+ function describeEdgeUrl(url) {
899
+ try {
900
+ var parsed = new URL(url);
901
+ return "".concat(parsed.origin).concat(parsed.pathname);
902
+ } catch (unused) {
903
+ var _url_split_;
904
+ return (_url_split_ = url.split("?")[0]) !== null && _url_split_ !== void 0 ? _url_split_ : "";
905
+ }
906
+ }
873
907
  // src/utils/trackingIngest.ts
874
908
  var DEFAULT_TRACKING_INGEST_CONFIG = {
875
909
  enabled: true,
@@ -1457,6 +1491,8 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
1457
1491
  this.controllerCallInFlight = false;
1458
1492
  this.playbackStartedForCurrentRequest = false;
1459
1493
  this.opportunityEpoch = -1;
1494
+ this.edgeExhaustedForBreak = false;
1495
+ this.edgeRequestsInBreak = 0;
1460
1496
  this.adPodQueue = [];
1461
1497
  this.defaultMinRemainingMsToRequestAd = 1e4;
1462
1498
  this.defaultMaxNoFillsPerBreak = 2;
@@ -1607,7 +1643,9 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
1607
1643
  _this.timing.clearAdRequestWatchdog();
1608
1644
  _this.timing.activeAdRequestToken = null;
1609
1645
  _this.showAds = true;
1610
- _this.timing.resetGamNoFillCounter();
1646
+ if (_this.currentPodSource !== "edge") {
1647
+ _this.timing.resetGamNoFillCounter();
1648
+ }
1611
1649
  if (_this.inAdBreak && _this.timing.expectedAdBreakDurationMs != null && _this.timing.getAdBreakEndWallClockMs() == null) {
1612
1650
  _this.timing.scheduleAdStopAtBreakBoundary();
1613
1651
  if (_this.debug) {
@@ -1739,6 +1777,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
1739
1777
  key: "beginVmapBreak",
1740
1778
  value: function beginVmapBreak(breakKey, position) {
1741
1779
  this.breakSource = "vmap";
1780
+ this.resetEdgeStateForBreak();
1742
1781
  this.followUpBaseVastUrl = void 0;
1743
1782
  this.unusableResponsesInBreak = 0;
1744
1783
  this.timing.totalAdRequestsInBreak = 0;
@@ -1750,7 +1789,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
1750
1789
  key: "handleAdStart",
1751
1790
  value: function handleAdStart(marker) {
1752
1791
  return _async_to_generator(function() {
1753
- var baseVastUrl, adBreakDurationMs, currentMuted, currentVolume, epoch, hasPrefetchedUrl, mode, podPlayed;
1792
+ var baseVastUrl, edgeConfigured, adBreakDurationMs, currentMuted, currentVolume, epoch, hasPrefetchedUrl, mode, edgeUrl, outcome, podPlayed;
1754
1793
  return _ts_generator(this, function(_state) {
1755
1794
  switch(_state.label){
1756
1795
  case 0:
@@ -1764,7 +1803,8 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
1764
1803
  ];
1765
1804
  }
1766
1805
  baseVastUrl = this.adConfig.apiVastTagUrl;
1767
- if (!baseVastUrl) {
1806
+ edgeConfigured = !!this.host.getEdgePodBaseUrl();
1807
+ if (!baseVastUrl && !edgeConfigured) {
1768
1808
  console.warn("[AdBreak] SCTE-35 break started but no direct VAST tag is configured");
1769
1809
  this.clearPendingAdBreak();
1770
1810
  return [
@@ -1780,6 +1820,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
1780
1820
  this.breakSource = "scte35";
1781
1821
  this.followUpBaseVastUrl = baseVastUrl;
1782
1822
  this.unusableResponsesInBreak = 0;
1823
+ this.resetEdgeStateForBreak();
1783
1824
  this.setSlotState("NEEDS_NEXT_AD", "scte35_break_start");
1784
1825
  if (this.adConfig.podAssignedByPrefetch) {
1785
1826
  this.adConfig.podAssignedByPrefetch = false;
@@ -1801,7 +1842,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
1801
1842
  this.noteOpportunity("scte35", this.cueManager.activeScte35BreakKey, "midroll");
1802
1843
  epoch = this.timing.adBreakEpoch;
1803
1844
  hasPrefetchedUrl = !!(this.pendingAdBreak && this.pendingAdBreak.vastUrls.length > 0);
1804
- if (!(adBreakDurationMs == null && !hasPrefetchedUrl)) return [
1845
+ if (!(adBreakDurationMs == null && (!hasPrefetchedUrl || edgeConfigured))) return [
1805
1846
  3,
1806
1847
  2
1807
1848
  ];
@@ -1820,11 +1861,46 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
1820
1861
  mode = this.host.config.isLiveStream ? "LIVE" : "VOD";
1821
1862
  console.log("[AdBreak] ".concat(mode, " break start: target duration=").concat(adBreakDurationMs != null ? "".concat(adBreakDurationMs, "ms") : "unknown"));
1822
1863
  }
1864
+ if (!edgeConfigured) return [
1865
+ 3,
1866
+ 5
1867
+ ];
1868
+ edgeUrl = this.canRequestEdge() ? this.prepareEdgePodRequest() : void 0;
1869
+ if (!edgeUrl) return [
1870
+ 3,
1871
+ 4
1872
+ ];
1873
+ this.clearPendingAdBreak();
1823
1874
  return [
1824
1875
  4,
1825
- this.startOptimizedPod(baseVastUrl, adBreakDurationMs)
1876
+ this.runEdgePod(edgeUrl, "edge_break_start")
1826
1877
  ];
1827
1878
  case 3:
1879
+ outcome = _state.sent();
1880
+ this.continueAfterEdgeOutcome(outcome, epoch);
1881
+ return [
1882
+ 2
1883
+ ];
1884
+ case 4:
1885
+ if (!this.edgeExhaustedForBreak) {
1886
+ this.markEdgeExhausted("not_eligible_at_break_start", {
1887
+ isLive: this.isLiveBreak(),
1888
+ durationMs: this.timing.expectedAdBreakDurationMs
1889
+ });
1890
+ }
1891
+ _state.label = 5;
1892
+ case 5:
1893
+ if (!baseVastUrl) {
1894
+ this.requestNextAd("no_gam_source");
1895
+ return [
1896
+ 2
1897
+ ];
1898
+ }
1899
+ return [
1900
+ 4,
1901
+ this.startOptimizedPod(baseVastUrl, adBreakDurationMs)
1902
+ ];
1903
+ case 6:
1828
1904
  podPlayed = _state.sent();
1829
1905
  if (!podPlayed && this.inAdBreak && epoch === this.timing.adBreakEpoch) {
1830
1906
  this.setSlotState("NEEDS_NEXT_AD", "initial_request_failed");
@@ -1916,6 +1992,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
1916
1992
  adPlayer = this.host.getAdPlayer();
1917
1993
  requestToken = ++this.timing.adRequestTokenCounter;
1918
1994
  this.timing.activeAdRequestToken = requestToken;
1995
+ this.currentPodSource = "gam";
1919
1996
  this.setSlotState("REQUESTING", "initial_pod");
1920
1997
  this.beginFunnelRequest(adPlayer, requestToken, "initial_pod");
1921
1998
  requestUrl = this.applyOpportunityFloor(podUrl);
@@ -2032,6 +2109,224 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
2032
2109
  }).call(this);
2033
2110
  }
2034
2111
  },
2112
+ {
2113
+ // ───────────────────────────────────────────────────────────────
2114
+ // Edge-first pods (live SCTE-35 only, GAM fallback)
2115
+ // ───────────────────────────────────────────────────────────────
2116
+ key: "resetEdgeStateForBreak",
2117
+ value: function resetEdgeStateForBreak() {
2118
+ this.edgeExhaustedForBreak = false;
2119
+ this.edgeRequestsInBreak = 0;
2120
+ this.currentPodSource = void 0;
2121
+ }
2122
+ },
2123
+ {
2124
+ key: "isLiveBreak",
2125
+ value: function isLiveBreak() {
2126
+ return this.host.isLiveStream() || this.host.config.isLiveStream === true;
2127
+ }
2128
+ },
2129
+ {
2130
+ key: "canRequestEdge",
2131
+ value: function canRequestEdge() {
2132
+ return this.breakSource === "scte35" && !this.edgeExhaustedForBreak && !!this.host.getEdgePodBaseUrl() && this.isLiveBreak() && this.timing.expectedAdBreakDurationMs != null && this.edgeRequestsInBreak < this.timing.maxTotalAdRequestsPerBreak;
2133
+ }
2134
+ },
2135
+ {
2136
+ key: "markEdgeExhausted",
2137
+ value: function markEdgeExhausted(reason) {
2138
+ var extra = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {};
2139
+ this.edgeExhaustedForBreak = true;
2140
+ this.countFunnelStage("edge_fallback:".concat(reason));
2141
+ this.logOrchestratorFunnel("edge_exhausted", _object_spread({
2142
+ reason: reason,
2143
+ edgeRequests: this.edgeRequestsInBreak,
2144
+ remainingMs: this.timing.getRemainingAdMs()
2145
+ }, extra));
2146
+ }
2147
+ },
2148
+ {
2149
+ /**
2150
+ * Builds the next Edge request from the budget left right now, or marks Edge
2151
+ * exhausted for this break. Synchronous on purpose: callers must reach the
2152
+ * REQUESTING state in the same tick so a second completion event cannot race
2153
+ * in a parallel request.
2154
+ */ key: "prepareEdgePodRequest",
2155
+ value: function prepareEdgePodRequest() {
2156
+ var baseUrl = this.host.getEdgePodBaseUrl();
2157
+ if (!baseUrl) return void 0;
2158
+ var remainingMs = this.timing.getRemainingAdMs();
2159
+ var remainingAds = Math.min(EDGE_POD_MAX_ADS, this.adConfig.resolvePodParams().maxAds - this.timing.totalAdsInBreak);
2160
+ if (remainingAds < 1) {
2161
+ this.markEdgeExhausted("ad_capacity_reached", {
2162
+ adsInBreak: this.timing.totalAdsInBreak
2163
+ });
2164
+ return void 0;
2165
+ }
2166
+ if (remainingMs < this.getMinRemainingMsToRequestAd()) {
2167
+ this.markEdgeExhausted("insufficient_break_time", {
2168
+ remainingMs: remainingMs
2169
+ });
2170
+ return void 0;
2171
+ }
2172
+ try {
2173
+ return buildEdgePodUrl(baseUrl, remainingMs, remainingAds);
2174
+ } catch (unused) {
2175
+ this.markEdgeExhausted("invalid_edge_url");
2176
+ return void 0;
2177
+ }
2178
+ }
2179
+ },
2180
+ {
2181
+ key: "runEdgePod",
2182
+ value: function runEdgePod(requestUrl, trigger) {
2183
+ return _async_to_generator(function() {
2184
+ var _params_get, _params_get1, epoch, generation, adPlayer, requestToken, params, _ref, _adPlayer_getPodAdCount, _adPlayer_getPodTotalDurationMs, remainingMs, remainingAds, podCount, error, errorMessage, classification, diag;
2185
+ return _ts_generator(this, function(_state) {
2186
+ switch(_state.label){
2187
+ case 0:
2188
+ epoch = this.timing.adBreakEpoch;
2189
+ this.recreateAdController("new_request");
2190
+ generation = this.controllerGeneration;
2191
+ adPlayer = this.host.getAdPlayer();
2192
+ requestToken = ++this.timing.adRequestTokenCounter;
2193
+ this.timing.activeAdRequestToken = requestToken;
2194
+ this.currentPodSource = "edge";
2195
+ this.edgeRequestsInBreak++;
2196
+ this.setSlotState("REQUESTING", trigger);
2197
+ this.beginFunnelRequest(adPlayer, requestToken, trigger, "edge");
2198
+ this.currentVastUrl = requestUrl;
2199
+ params = new URL(requestUrl).searchParams;
2200
+ this.logOrchestratorFunnel("edge_request_sent", {
2201
+ trigger: trigger,
2202
+ attempt: this.edgeRequestsInBreak,
2203
+ endpoint: describeEdgeUrl(requestUrl),
2204
+ pmxd: (_params_get = params.get("pmxd")) !== null && _params_get !== void 0 ? _params_get : void 0,
2205
+ pmad: (_params_get1 = params.get("pmad")) !== null && _params_get1 !== void 0 ? _params_get1 : void 0
2206
+ });
2207
+ _state.label = 1;
2208
+ case 1:
2209
+ _state.trys.push([
2210
+ 1,
2211
+ 4,
2212
+ ,
2213
+ 5
2214
+ ]);
2215
+ this.timing.startAdRequestWatchdog(requestToken);
2216
+ return [
2217
+ 4,
2218
+ this.callController(function() {
2219
+ return adPlayer.requestAds(requestUrl, {
2220
+ source: "edge"
2221
+ });
2222
+ })
2223
+ ];
2224
+ case 2:
2225
+ _state.sent();
2226
+ this.timing.clearAdRequestWatchdog();
2227
+ if (this.isStale(epoch, generation)) {
2228
+ this.logOrchestratorFunnel("stale_request_result_ignored", {
2229
+ requestId: requestToken,
2230
+ source: "edge"
2231
+ });
2232
+ return [
2233
+ 2,
2234
+ "stale"
2235
+ ];
2236
+ }
2237
+ remainingMs = this.timing.getRemainingAdMs();
2238
+ remainingAds = Math.max(1, Math.min(EDGE_POD_MAX_ADS, this.adConfig.resolvePodParams().maxAds - this.timing.totalAdsInBreak));
2239
+ podCount = adPlayer.limitPodToBudget ? adPlayer.limitPodToBudget(remainingMs, remainingAds) : (_ref = (_adPlayer_getPodAdCount = adPlayer.getPodAdCount) === null || _adPlayer_getPodAdCount === void 0 ? void 0 : _adPlayer_getPodAdCount.call(adPlayer)) !== null && _ref !== void 0 ? _ref : 0;
2240
+ if (classifyEdgeVastResponse({
2241
+ podAdCount: podCount
2242
+ }) !== "fill") {
2243
+ throw new Error("".concat(EDGE_POD_EXCEEDS_BUDGET, ": no Edge ad fits ").concat(remainingMs, "ms"));
2244
+ }
2245
+ this.setSlotState("READY", "edge_vast_ready");
2246
+ this.logOrchestratorFunnel("edge_fill", {
2247
+ podAdCount: podCount,
2248
+ podDurationMs: (_adPlayer_getPodTotalDurationMs = adPlayer.getPodTotalDurationMs) === null || _adPlayer_getPodTotalDurationMs === void 0 ? void 0 : _adPlayer_getPodTotalDurationMs.call(adPlayer),
2249
+ remainingMs: remainingMs
2250
+ });
2251
+ sendAdLoadedTracking(this.host.config.licenseKey, {
2252
+ source: "hls",
2253
+ vastUrl: requestUrl,
2254
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
2255
+ }, this.analyticsContext).catch(function() {});
2256
+ this.timing.startAdFailsafeTimer(requestToken);
2257
+ return [
2258
+ 4,
2259
+ this.callController(function() {
2260
+ return adPlayer.play();
2261
+ })
2262
+ ];
2263
+ case 3:
2264
+ _state.sent();
2265
+ if (this.isStale(epoch, generation)) return [
2266
+ 2,
2267
+ "stale"
2268
+ ];
2269
+ if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {
2270
+ this.timing.scheduleAdStopAtBreakBoundary();
2271
+ }
2272
+ adPlayer.setAdVolume(getAdAudioVolume(adPlayer));
2273
+ return [
2274
+ 2,
2275
+ "fill"
2276
+ ];
2277
+ case 4:
2278
+ error = _state.sent();
2279
+ this.timing.clearAdRequestWatchdog();
2280
+ this.timing.clearAdFailsafeTimer();
2281
+ if (this.timing.activeAdRequestToken === requestToken) {
2282
+ this.timing.activeAdRequestToken = null;
2283
+ }
2284
+ errorMessage = error === null || error === void 0 ? void 0 : error.message;
2285
+ if (this.isStale(epoch, generation)) {
2286
+ this.logOrchestratorFunnel("stale_request_result_ignored", {
2287
+ requestId: requestToken,
2288
+ source: "edge",
2289
+ errorMessage: errorMessage
2290
+ });
2291
+ return [
2292
+ 2,
2293
+ "stale"
2294
+ ];
2295
+ }
2296
+ classification = classifyEdgeVastResponse({
2297
+ podAdCount: 0,
2298
+ error: error
2299
+ });
2300
+ diag = this.requestDiagnostics.get(requestToken);
2301
+ if (diag && !diag.failureReason) diag.failureReason = "edge_".concat(classification, ": ").concat(errorMessage);
2302
+ this.markEdgeExhausted("edge_".concat(classification), {
2303
+ errorMessage: errorMessage
2304
+ });
2305
+ return [
2306
+ 2,
2307
+ "fallback"
2308
+ ];
2309
+ case 5:
2310
+ return [
2311
+ 2
2312
+ ];
2313
+ }
2314
+ });
2315
+ }).call(this);
2316
+ }
2317
+ },
2318
+ {
2319
+ key: "continueAfterEdgeOutcome",
2320
+ value: function continueAfterEdgeOutcome(outcome, epoch) {
2321
+ if (outcome !== "fallback") return;
2322
+ if (!this.inAdBreak || epoch !== this.timing.adBreakEpoch) return;
2323
+ if (this.debug) {
2324
+ console.log("[AdBreak][EDGE] No usable Edge pod; checking with GAM for the remaining break");
2325
+ }
2326
+ this.setSlotState("NEEDS_NEXT_AD", "edge_fallback");
2327
+ this.requestNextAd("edge_fallback");
2328
+ }
2329
+ },
2035
2330
  {
2036
2331
  // ───────────────────────────────────────────────────────────────
2037
2332
  // Next-ad scheduling (single flight)
@@ -2087,7 +2382,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
2087
2382
  value: function getStopRequestingReason(remainingMs) {
2088
2383
  if (this.breakSource === "vmap") {
2089
2384
  if (this.adPodQueue.length === 0) return "vmap_queue_empty";
2090
- } else if (this.breakSource !== "scte35" || !this.followUpBaseVastUrl) {
2385
+ } else if (this.breakSource !== "scte35" || !this.followUpBaseVastUrl && !this.canRequestEdge()) {
2091
2386
  return "no_follow_up_source";
2092
2387
  }
2093
2388
  if (this.timing.consecutiveFailures >= this.timing.maxConsecutiveFailures) {
@@ -2099,7 +2394,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
2099
2394
  if (this.timing.totalAdRequestsInBreak >= this.timing.maxTotalAdRequestsPerBreak) {
2100
2395
  return "request_cap";
2101
2396
  }
2102
- if (this.timing.isGamInCooldown()) {
2397
+ if (!this.canRequestEdge() && this.timing.isGamInCooldown()) {
2103
2398
  return "gam_cooldown";
2104
2399
  }
2105
2400
  if (this.isBreakTimeBounded() && remainingMs < this.getMinRemainingMsToRequestAd()) {
@@ -2193,10 +2488,33 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
2193
2488
  key: "requestFollowUpAd",
2194
2489
  value: function requestFollowUpAd(trigger) {
2195
2490
  return _async_to_generator(function() {
2196
- var url, epoch, unused;
2491
+ var edgeUrl, epoch2, outcome, url, epoch, unused;
2197
2492
  return _ts_generator(this, function(_state) {
2198
2493
  switch(_state.label){
2199
2494
  case 0:
2495
+ if (!this.canRequestEdge()) return [
2496
+ 3,
2497
+ 2
2498
+ ];
2499
+ edgeUrl = this.prepareEdgePodRequest();
2500
+ if (!edgeUrl) {
2501
+ this.requestNextAd("".concat(trigger, ":edge_unavailable"));
2502
+ return [
2503
+ 2
2504
+ ];
2505
+ }
2506
+ epoch2 = this.timing.adBreakEpoch;
2507
+ return [
2508
+ 4,
2509
+ this.runEdgePod(edgeUrl, "edge_".concat(trigger))
2510
+ ];
2511
+ case 1:
2512
+ outcome = _state.sent();
2513
+ this.continueAfterEdgeOutcome(outcome, epoch2);
2514
+ return [
2515
+ 2
2516
+ ];
2517
+ case 2:
2200
2518
  if (this.breakSource === "vmap") {
2201
2519
  url = this.adPodQueue.shift();
2202
2520
  } else if (this.followUpBaseVastUrl) {
@@ -2207,32 +2525,32 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
2207
2525
  ];
2208
2526
  this.noteVastUrlGenerated(ensureVideoAdType(url), trigger);
2209
2527
  epoch = this.timing.adBreakEpoch;
2210
- _state.label = 1;
2211
- case 1:
2528
+ _state.label = 3;
2529
+ case 3:
2212
2530
  _state.trys.push([
2213
- 1,
2214
2531
  3,
2532
+ 5,
2215
2533
  ,
2216
- 4
2534
+ 6
2217
2535
  ]);
2218
2536
  return [
2219
2537
  4,
2220
2538
  this.playSingleAd(url, trigger)
2221
2539
  ];
2222
- case 2:
2540
+ case 4:
2223
2541
  _state.sent();
2224
2542
  return [
2225
2543
  3,
2226
- 4
2544
+ 6
2227
2545
  ];
2228
- case 3:
2546
+ case 5:
2229
2547
  unused = _state.sent();
2230
2548
  this.continueAfterRequestFailure(epoch);
2231
2549
  return [
2232
2550
  3,
2233
- 4
2551
+ 6
2234
2552
  ];
2235
- case 4:
2553
+ case 6:
2236
2554
  return [
2237
2555
  2
2238
2556
  ];
@@ -2345,7 +2663,11 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
2345
2663
  case "gam_http_request_sent":
2346
2664
  diag.gamRequestSent++;
2347
2665
  break;
2666
+ case "edge_http_request_sent":
2667
+ diag.edgeRequestSent++;
2668
+ break;
2348
2669
  case "gam_response_received":
2670
+ case "edge_response_received":
2349
2671
  diag.vastHttpStatus = payload.httpStatus;
2350
2672
  break;
2351
2673
  case "vast_parsed":
@@ -2532,6 +2854,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
2532
2854
  {
2533
2855
  key: "beginFunnelRequest",
2534
2856
  value: function beginFunnelRequest(adPlayer, requestToken, trigger) {
2857
+ var source = arguments.length > 3 && arguments[3] !== void 0 ? arguments[3] : "gam";
2535
2858
  var _adPlayer_setFunnelContext;
2536
2859
  if (this.controllerCallInFlight) {
2537
2860
  this.logOrchestratorFunnel("duplicate_request_detected", {
@@ -2546,7 +2869,9 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
2546
2869
  requestId: requestToken,
2547
2870
  controllerId: this.controllerGeneration,
2548
2871
  trigger: trigger,
2872
+ source: source,
2549
2873
  gamRequestSent: 0,
2874
+ edgeRequestSent: 0,
2550
2875
  wrapperDepth: 0,
2551
2876
  inlineResolved: false,
2552
2877
  mediaCandidateCount: 0,
@@ -2677,6 +3002,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
2677
3002
  this.timing.activeAdRequestToken = requestToken;
2678
3003
  ap = this.host.getAdPlayer();
2679
3004
  budgetedUrl = this.withCurrentPodBudget(vastTagUrl);
3005
+ this.currentPodSource = "gam";
2680
3006
  this.setSlotState("REQUESTING", trigger);
2681
3007
  this.beginFunnelRequest(ap, requestToken, trigger);
2682
3008
  requestUrl = this.applyOpportunityFloor(budgetedUrl);
@@ -2824,6 +3150,10 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
2824
3150
  requests: requests,
2825
3151
  requestRetries: requestRetries,
2826
3152
  gamResponses: count("gam_response_received"),
3153
+ edgeRequests: count("edge_request_sent"),
3154
+ edgeFills: count("edge_fill"),
3155
+ edgeEmptySentinels: count("edge_empty_sentinel"),
3156
+ edgeFallbacks: countPrefix("edge_fallback:"),
2827
3157
  requestsSuppressed: count("request_suppressed"),
2828
3158
  duplicates: count("duplicate_request_detected") + count("duplicate_break_start"),
2829
3159
  filled: count("vast_received"),
@@ -2920,6 +3250,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
2920
3250
  this.breakSource = void 0;
2921
3251
  this.followUpBaseVastUrl = void 0;
2922
3252
  this.unusableResponsesInBreak = 0;
3253
+ this.resetEdgeStateForBreak();
2923
3254
  adPlayer.stop().catch(function() {});
2924
3255
  if (!alreadyEnded) this.logBreakFunnel(reason);
2925
3256
  this.opportunityFloor = void 0;
@@ -3233,7 +3564,13 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
3233
3564
  });
3234
3565
  return;
3235
3566
  }
3236
- this.timing.consecutiveFailures++;
3567
+ if (this.currentPodSource === "edge") {
3568
+ this.markEdgeExhausted("edge_playback_failed", {
3569
+ reason: reason
3570
+ });
3571
+ } else {
3572
+ this.timing.consecutiveFailures++;
3573
+ }
3237
3574
  var diag = this.currentRequestId != null ? this.requestDiagnostics.get(this.currentRequestId) : void 0;
3238
3575
  if (diag && !diag.failureReason) diag.failureReason = reason;
3239
3576
  if (this.debug) {