@dolby-optiview/ads-sdk-core 1.2.1 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/dist/OptiViewAds.d.ts +5 -4
  2. package/dist/OptiViewAds.js +1 -1
  3. package/dist/OptiViewAdsExternalVendors.d.ts +32 -0
  4. package/dist/external-playback/index.d.ts +4 -0
  5. package/dist/external-playback/index.js +1 -0
  6. package/dist/external-playback/observeExternalPlayback.d.ts +19 -0
  7. package/dist/external-playback/observeExternalPlayback.js +1 -0
  8. package/dist/external-playback/reconnectHold.d.ts +19 -0
  9. package/dist/external-playback/reconnectHold.js +1 -0
  10. package/dist/generated/diagnosticCodes.d.ts +46 -6
  11. package/dist/generated/diagnosticCodes.js +1 -1
  12. package/dist/generated/version.d.ts +1 -1
  13. package/dist/generated/version.js +1 -1
  14. package/dist/index.d.ts +7 -5
  15. package/dist/index.js +1 -1
  16. package/dist/interfaces/AdSourceQueue.d.ts +19 -0
  17. package/dist/interfaces/AdSourceQueue.js +1 -0
  18. package/dist/interfaces/PlayerAdapter.d.ts +31 -1
  19. package/dist/internal/index.d.ts +2 -0
  20. package/dist/internal/index.js +1 -1
  21. package/dist/services/AdBreakSequencer.d.ts +33 -4
  22. package/dist/services/AdBreakSequencer.js +1 -1
  23. package/dist/services/AdPlayerController.d.ts +12 -73
  24. package/dist/services/AdPlayerController.js +1 -1
  25. package/dist/services/BreakChain.d.ts +43 -0
  26. package/dist/services/BreakChain.js +1 -0
  27. package/dist/services/BreakChainRecorder.d.ts +63 -0
  28. package/dist/services/BreakChainRecorder.js +1 -0
  29. package/dist/services/BreakPreloadLedger.d.ts +13 -0
  30. package/dist/services/BreakPreloadLedger.js +1 -0
  31. package/dist/services/BreakScheduler.d.ts +20 -6
  32. package/dist/services/BreakScheduler.js +1 -1
  33. package/dist/services/BreakTransitionTimer.d.ts +27 -2
  34. package/dist/services/BreakTransitionTimer.js +1 -1
  35. package/dist/services/CmcdAdReporter.d.ts +1 -0
  36. package/dist/services/CmcdAdReporter.js +1 -1
  37. package/dist/services/ExternalPlaybackGate.d.ts +40 -0
  38. package/dist/services/ExternalPlaybackGate.js +1 -0
  39. package/dist/services/LiveStartPushbackController.d.ts +5 -10
  40. package/dist/services/LiveStartPushbackController.js +1 -1
  41. package/dist/services/ManifestService.d.ts +2 -1
  42. package/dist/services/ManifestService.js +1 -1
  43. package/dist/services/ReloadGuard.d.ts +11 -0
  44. package/dist/services/ReloadGuard.js +1 -0
  45. package/dist/services/RewindRearm.d.ts +3 -1
  46. package/dist/services/RewindRearm.js +1 -1
  47. package/dist/services/SsaiController.js +1 -1
  48. package/dist/services/adPlayer/AdPlayerCallbacks.d.ts +63 -0
  49. package/dist/services/adPlayer/AdPlayerCallbacks.js +1 -0
  50. package/dist/services/adPlayer/AdPlayerEvents.d.ts +3 -7
  51. package/dist/services/adPlayer/AdPlayerEvents.js +1 -1
  52. package/dist/services/adPlayer/AdPlayerState.d.ts +0 -1
  53. package/dist/services/adPlayer/AdPlayerState.js +1 -1
  54. package/dist/services/adPlayer/AdPreloader.d.ts +2 -2
  55. package/dist/services/adPlayer/AdPreloader.js +1 -1
  56. package/dist/services/adPlayer/AdStage.d.ts +7 -4
  57. package/dist/services/adPlayer/AdStage.js +1 -1
  58. package/dist/services/adPlayer/AssetParameterResolver.d.ts +10 -2
  59. package/dist/services/adPlayer/AssetParameterResolver.js +1 -1
  60. package/dist/services/adPlayer/AssetPlayback.d.ts +9 -2
  61. package/dist/services/adPlayer/AssetPlayback.js +1 -1
  62. package/dist/services/adPlayer/AudioController.d.ts +58 -0
  63. package/dist/services/adPlayer/AudioController.js +1 -0
  64. package/dist/services/adPlayer/BreakSequenceDriver.d.ts +9 -3
  65. package/dist/services/adPlayer/BreakSequenceDriver.js +1 -1
  66. package/dist/services/adPlayer/ContentResumeCoordinator.d.ts +2 -0
  67. package/dist/services/adPlayer/ContentResumeCoordinator.js +1 -1
  68. package/dist/services/adPlayer/GamPodBridge.d.ts +11 -1
  69. package/dist/services/adPlayer/GamPodBridge.js +1 -1
  70. package/dist/services/adPlayer/PauseAdPresenter.d.ts +3 -1
  71. package/dist/services/adPlayer/PauseAdPresenter.js +1 -1
  72. package/dist/services/adPlayer/PreparedPresentation.js +1 -1
  73. package/dist/services/adPlayer/PresentationModeTracker.d.ts +1 -1
  74. package/dist/services/adPlayer/PresentationModeTracker.js +1 -1
  75. package/dist/services/adPlayer/SharedElementBreakPlayer.d.ts +3 -0
  76. package/dist/services/adPlayer/SharedElementBreakPlayer.js +1 -1
  77. package/dist/services/adPlayer/SuccessorQueue.d.ts +36 -0
  78. package/dist/services/adPlayer/SuccessorQueue.js +1 -0
  79. package/dist/services/adPlayer/SurfaceRevealReporter.d.ts +28 -0
  80. package/dist/services/adPlayer/SurfaceRevealReporter.js +1 -0
  81. package/dist/services/adPlayer/presentation/DomPresentationHost.d.ts +22 -0
  82. package/dist/services/adPlayer/presentation/DomPresentationHost.js +1 -0
  83. package/dist/services/adPlayer/presentation/LayoutTransform.d.ts +28 -0
  84. package/dist/services/adPlayer/presentation/LayoutTransform.js +1 -0
  85. package/dist/services/adPlayer/presentation/PresentationLayout.d.ts +19 -0
  86. package/dist/services/adPlayer/presentation/PresentationLayout.js +1 -0
  87. package/dist/services/assetParameterMacroTable.js +1 -1
  88. package/dist/services/assetParameterMacros.d.ts +9 -9
  89. package/dist/services/assetParameterMacros.js +1 -1
  90. package/dist/services/channelSwitch.d.ts +2 -2
  91. package/dist/services/externalPlaybackDiagnostic.d.ts +3 -0
  92. package/dist/services/externalPlaybackDiagnostic.js +1 -0
  93. package/dist/services/externalPlaybackGuard.d.ts +24 -0
  94. package/dist/services/externalPlaybackGuard.js +1 -0
  95. package/dist/services/parseBreakManifest.d.ts +2 -1
  96. package/dist/services/parseBreakManifest.js +1 -1
  97. package/dist/services/resolveChannelEvents.d.ts +17 -0
  98. package/dist/services/resolveChannelEvents.js +1 -0
  99. package/dist/services/resolveExternalVendorConfigurations.js +1 -1
  100. package/dist/services/resolveGamVendorConfiguration.d.ts +6 -1
  101. package/dist/services/resolveGamVendorConfiguration.js +1 -1
  102. package/dist/services/ssbStream.d.ts +39 -0
  103. package/dist/services/ssbStream.js +1 -0
  104. package/dist/services/ssbSwitchSignals.d.ts +15 -0
  105. package/dist/services/ssbSwitchSignals.js +1 -0
  106. package/dist/services/staticAssetUrl.d.ts +28 -0
  107. package/dist/services/staticAssetUrl.js +1 -0
  108. package/dist/session/AdBreakStatusTracker.d.ts +18 -0
  109. package/dist/session/AdBreakStatusTracker.js +1 -1
  110. package/dist/session/BreakLifecycle.d.ts +6 -0
  111. package/dist/session/BreakLifecycle.js +1 -1
  112. package/dist/session/SessionStarter.d.ts +6 -43
  113. package/dist/session/SessionStarter.js +1 -1
  114. package/dist/session/SessionStarterDeps.d.ts +39 -0
  115. package/dist/session/SessionStarterDeps.js +1 -0
  116. package/dist/session/SessionStarterHelpers.d.ts +36 -0
  117. package/dist/session/SessionStarterHelpers.js +1 -0
  118. package/dist/session/SessionState.d.ts +22 -2
  119. package/dist/session/SessionState.js +1 -1
  120. package/dist/session/SsbSwitch.d.ts +55 -0
  121. package/dist/session/SsbSwitch.js +1 -0
  122. package/dist/session/breakMeasurement.d.ts +16 -0
  123. package/dist/session/breakMeasurement.js +1 -0
  124. package/dist/session/contentSnapback.d.ts +13 -0
  125. package/dist/session/contentSnapback.js +1 -0
  126. package/dist/session/createExternalPlaybackGate.d.ts +19 -0
  127. package/dist/session/createExternalPlaybackGate.js +1 -0
  128. package/dist/session/sessionTick.d.ts +7 -0
  129. package/dist/session/sessionTick.js +1 -0
  130. package/dist/types/BreakManifest.d.ts +58 -5
  131. package/dist/types/Events.d.ts +21 -10
  132. package/dist/types/ExternalVendorConfiguration.d.ts +3 -1
  133. package/dist/types/OptiViewAdsConfig.d.ts +24 -8
  134. package/package.json +5 -1
@@ -12,62 +12,10 @@ import type { PauseAdResolution } from './PauseAdController';
12
12
  import { type Clock } from './BreakScheduler';
13
13
  import { PauseAdPresenter } from './adPlayer/PauseAdPresenter';
14
14
  import { AssetParameterResolver } from './adPlayer/AssetParameterResolver';
15
+ import { GamPodBridge } from './adPlayer/GamPodBridge';
15
16
  import type { BreakEndOnStreamClock } from './adPlayer/streamClockResume';
16
- /** Creative identity IMA reports for an ad of a GAM pod; absent for other asset kinds. */
17
- export interface AdCreativeIdentity {
18
- adId?: string | undefined;
19
- creativeId?: string | undefined;
20
- /** 1-based position within the pod (IMA `getAdPodInfo().getAdPosition()` / VAST `sequence`); absent when unreported. */
21
- podPosition?: number | undefined;
22
- }
23
- export interface AdPlayerCallbacks {
24
- onAdStart: (breakInfo: Break, asset: Asset, index: number, total: number, identity?: AdCreativeIdentity) => void;
25
- onAdEnd: (breakInfo: Break, asset: Asset, index: number, total: number, identity?: AdCreativeIdentity) => void;
26
- /** `asset` is undefined for a break-level error (no playable variant, the break never began). */
27
- onAdError: (breakInfo: Break, asset: Asset | undefined, error: Error) => void;
28
- onAdTimeUpdate: (breakInfo: Break, asset: Asset, currentTime: number, duration: number) => void;
29
- onAdWaiting: (breakInfo: Break, asset: Asset) => void;
30
- onAdPlaying: (breakInfo: Break, asset: Asset) => void;
31
- onBreakEnd: (breakInfo: Break) => void;
32
- /** The break-cut timer expired with an ad still in flight; fired before that ad's `adend`. */
33
- onBreakCut?: (breakInfo: Break) => void;
34
- /**
35
- * A content-covering break ended before its content pause landed, so no player transition
36
- * announces the resume. Only the deferred-pause renderers (Kotlin/Swift) fire this.
37
- */
38
- onBreakEndedWithoutContentPause?: (breakInfo: Break) => void;
39
- /** Ad overlay became visible; gates `adbreakbegin` so the AD badge never precedes the first ad frame. */
40
- onBreakVisible?: (breakInfo: Break) => void;
41
- /** The current break soft-ended into a chained successor (overlay held, no content resume). */
42
- onChainedBreakBegin?: (next: Break) => void;
43
- /** A chained successor resolved but has nothing to play; the SDK must retire it (completeBreak). */
44
- onChainedBreakSkipped?: (next: Break) => void;
45
- /**
46
- * Fired just before the runtime's own post-break resume-seek, so the scheduler does not read
47
- * the backward step as a DVR seek-back and re-arm the break.
48
- */
49
- onContentResumeSeek?: (targetSeconds: number, breakEndSeconds: number) => void;
50
- /**
51
- * Brackets a content RELOAD (shared-element break end), so the playhead passing through the
52
- * start of the freshly loaded stream is not read as a seek back before the break.
53
- */
54
- onContentReloading?: (active: boolean) => void;
55
- }
56
- export interface PlayBreakOptions {
57
- /** Seconds to present the break for (tune-in breaks: the remaining time); defaults to `break.duration`. */
58
- effectiveDurationSec?: number;
59
- /** Defer the overlay and `onBreakVisible` until the first ad frame renders (held delay:0 fullscreen pre-rolls). */
60
- firstFrameGate?: boolean;
61
- }
62
- /**
63
- * IMA quartile callbacks. `adIndex`/`totalAds` attribute the milestone to one ad of a
64
- * multi-ad GAM pod; omitted when there is no per-ad breakdown.
65
- */
66
- export interface GamEventCallbacks {
67
- onAdFirstQuartile?: (breakInfo: Break, asset: Asset, adIndex?: number, totalAds?: number) => void;
68
- onAdMidpoint?: (breakInfo: Break, asset: Asset, adIndex?: number, totalAds?: number) => void;
69
- onAdThirdQuartile?: (breakInfo: Break, asset: Asset, adIndex?: number, totalAds?: number) => void;
70
- }
17
+ import type { AdCreativeIdentity, AdPlayerCallbacks, GamEventCallbacks, PlayBreakOptions } from './adPlayer/AdPlayerCallbacks';
18
+ export type { AdCreativeIdentity, AdPlayerCallbacks, GamEventCallbacks, PlayBreakOptions };
71
19
  export declare class AdPlayerController {
72
20
  private static readonly AD_START_BUDGET_MS;
73
21
  private static readonly GAM_POD_AD_START_BUDGET_MS;
@@ -79,6 +27,7 @@ export declare class AdPlayerController {
79
27
  private createAdAdapter;
80
28
  private adMediaContainer;
81
29
  private adPlayer;
30
+ private readonly surfaceReveal;
82
31
  private adContainer;
83
32
  private companionElement;
84
33
  private staticQuartilesFired;
@@ -99,12 +48,13 @@ export declare class AdPlayerController {
99
48
  private readonly state;
100
49
  readonly pauseAdPresenter: PauseAdPresenter;
101
50
  private readonly assetParameters;
102
- private readonly gamPodBridge;
51
+ readonly gamPodBridge: GamPodBridge;
103
52
  private readonly adStage;
104
53
  private destroyed;
105
54
  private readonly preloader;
106
55
  private readonly assetPlayback;
107
56
  private readonly breakSequenceDriver;
57
+ private readonly successorQueue;
108
58
  private readonly variantSelector;
109
59
  private readonly adPlayerEvents;
110
60
  private get gamEventCallbacks();
@@ -139,9 +89,7 @@ export declare class AdPlayerController {
139
89
  private readonly presentationModes;
140
90
  private readonly sharedElementPlayer;
141
91
  private readonly resumeCoordinator;
142
- private syncing;
143
- private contentVolumeHandler;
144
- private adVolumeChangeHandler;
92
+ private readonly audio;
145
93
  private volumeChangeCallback;
146
94
  private stage;
147
95
  private resizeObserver;
@@ -213,7 +161,6 @@ export declare class AdPlayerController {
213
161
  * not undo it: re-boxing a rolling creative would re-fetch a skipped companion and un-pause content.
214
162
  */
215
163
  private doubleBoxAudio;
216
- private audioFocus;
217
164
  constructor(contentPlayer: PlayerAdapter, container: HTMLElement, playerContainer: HTMLElement | undefined, createAdAdapter: CreateAdAdapter, debug?: boolean, gamConfig?: GamConfig, preloadMode?: ResolvedPreloadMode, insertionMode?: ResolvedInsertionMode, adBreakCutSafetyMarginSec?: number, doubleBoxAudio?: DoubleBoxAudio, singleSurfaceDevice?: boolean,
218
165
  /** Injected so drift correction is deterministic under fake timers; reuses the scheduler's `Clock`. */
219
166
  clock?: Clock, contentRecovery?: ResolvedContentRecovery);
@@ -235,6 +182,8 @@ export declare class AdPlayerController {
235
182
  private clearContentGate;
236
183
  /** `null` disables chaining: breaks then always hard-end regardless of how close the next one is. */
237
184
  setChainResolver(resolver: ((currentBreakId: string) => Break | null) | null): void;
185
+ /** The playing break's chained successor appeared, changed or disappeared: re-queue or drop its first ad. */
186
+ onChainSuccessorChanged(): void;
238
187
  /** {@link ContentResumeCoordinator.setCueAnchorResolver}. */
239
188
  setResumeCueAnchorResolver(resolver: ((breakInfo: Break) => number | null) | null): void;
240
189
  /** {@link ContentResumeCoordinator.setCueEndResolver}. */
@@ -279,6 +228,7 @@ export declare class AdPlayerController {
279
228
  setSessionAssetParameterMacros(macros: AssetParameterMacros | undefined): void;
280
229
  get sessionAssetParameterMacros(): AssetParameterMacros;
281
230
  private buildVastTagUrl;
231
+ private buildStaticAssetUrl;
282
232
  private pushAssetParameters;
283
233
  private setupOverlay;
284
234
  /** Always creates a fresh IMA session. */
@@ -413,11 +363,6 @@ export declare class AdPlayerController {
413
363
  declaredFormatOf(breakInfo: Break): BreakFormat | undefined;
414
364
  private getVariant;
415
365
  private applyLayout;
416
- /**
417
- * For double/lshape_ad/lshape_content, playerContainer keeps its break-time z-index (101)
418
- * during the transition so it stays above the still-fading adContainer (z:100) and companion
419
- * (z:99); otherwise the backdrop cuts to black instead of content growing back.
420
- */
421
366
  private restoreLayout;
422
367
  /**
423
368
  * Selection-aware: without the inputs, a device-targeted variant's assets would read as empty
@@ -488,20 +433,14 @@ export declare class AdPlayerController {
488
433
  private show;
489
434
  private hide;
490
435
  isAdPlaying(): boolean;
491
- /**
492
- * Reads/writes the effective audio owner — the ad player during a break, content otherwise.
493
- * The `volumechange` sync mirrors onto the other side; a CSAI ad is bridged via
494
- * {@link VastAdManager.setMuted} (IMA manages its own audio path). With an active
495
- * {@link audioFocus} only the focused side is targeted.
496
- */
436
+ /** The viewer's audio state, applied to both players; see {@link AudioController}. */
497
437
  get muted(): boolean;
498
438
  set muted(value: boolean);
499
- /** 0-1. See {@link muted} for which player is the effective target. */
439
+ /** 0-1. See {@link muted}. */
500
440
  get volume(): number;
501
441
  set volume(value: number);
502
442
  /** Invoked with the current {@link muted}/{@link volume} whenever either changes. Pass `null` to detach. */
503
443
  setVolumeChangeCallback(callback: ((muted: boolean, volume: number) => void) | null): void;
504
- private notifyVolumeChange;
505
444
  destroy(): void;
506
445
  private log;
507
446
  }
@@ -1 +1 @@
1
- import{BreakFormat as e,Timebase as t,isPostRollBreak as r}from"../types/BreakManifest";import{DEFAULT_AD_BREAK_CUT_SAFETY_MARGIN_SEC as s,DEFAULT_DOUBLE_BOX_AUDIO as a}from"../types/OptiViewAdsConfig";import{pipOverridesFormat as i,pipSuppressesBackdrop as n}from"./pictureInPicture";import{AdBreakSequencer as o,assetsOfVariant as d,resolveBreakFormat as l}from"./AdBreakSequencer";import{selectVariantDetailed as h}from"./selectVariant";import{debugLog as u}from"../diagnostics/debugLog";import{systemClock as c}from"./BreakScheduler";import{createAdPlayerState as m,invalidateMediaPreload as g}from"./adPlayer/AdPlayerState";import{PauseAdPresenter as P}from"./adPlayer/PauseAdPresenter";import{AssetParameterResolver as y}from"./adPlayer/AssetParameterResolver";import{GamPodBridge as p}from"./adPlayer/GamPodBridge";import{AdStage as A}from"./adPlayer/AdStage";import{AdPreloader as v}from"./adPlayer/AdPreloader";import{PresentationModeTracker as C}from"./adPlayer/PresentationModeTracker";import{SharedElementBreakPlayer as k}from"./adPlayer/SharedElementBreakPlayer";import{ContentResumeCoordinator as f}from"./adPlayer/ContentResumeCoordinator";import{resumeContentAfterBreak as S}from"./adPlayer/ContentRecovery";import{AssetPlayback as B}from"./adPlayer/AssetPlayback";import{BreakSequenceDriver as E}from"./adPlayer/BreakSequenceDriver";import{VariantSelector as b}from"./adPlayer/VariantSelector";import{AdPlayerEvents as F}from"./adPlayer/AdPlayerEvents";import{armPlaybackCutoff as T,clearPlaybackCutoff as M}from"./adPlayer/PlaybackCutoff";import{AdError as R}from"../types/AdError";export class AdPlayerController{get contentPaused(){return this.state.contentPaused}set contentPaused(e){this.state.contentPaused=e}get firstFrameGateActive(){return this.state.firstFrameGateActive}set firstFrameGateActive(e){this.state.firstFrameGateActive=e}get firstFrameGateShown(){return this.state.firstFrameGateShown}set firstFrameGateShown(e){this.state.firstFrameGateShown=e}get firstFrameRejector(){return this.state.firstFrameRejector}set firstFrameRejector(e){this.state.firstFrameRejector=e}get gamEventCallbacks(){return this.gamPodBridge.eventCallbacks}get vastAdManager(){return this.state.vastAdManager}set vastAdManager(e){this.state.vastAdManager=e}get currentBreak(){return this.state.currentBreak}set currentBreak(e){this.state.currentBreak=e}get currentAsset(){return this.state.currentAsset}set currentAsset(e){this.state.currentAsset=e}get callbacks(){return this.state.callbacks}set callbacks(e){this.state.callbacks=e}get isPlaying(){return this.state.isPlaying}set isPlaying(e){this.state.isPlaying=e}get effectiveBreakDurationSec(){return this.state.effectiveBreakDurationSec}set effectiveBreakDurationSec(e){this.state.effectiveBreakDurationSec=e}get adBreakCutSafetyMarginSec(){return this.state.adBreakCutSafetyMarginSec}set adBreakCutSafetyMarginSec(e){this.state.adBreakCutSafetyMarginSec=e}get durationTimer(){return this.state.durationTimer}set durationTimer(e){this.state.durationTimer=e}get adInsertionType(){return this.state.adInsertionType}set adInsertionType(e){this.state.adInsertionType=e}get breakTimebase(){return this.state.breakTimebase}set breakTimebase(e){this.state.breakTimebase=e}constructor(e,t,r,i,n=!1,o,d="parallel",l="overlay",h=s,u=a,S=!1,T=c,M="none"){this.playerContainerCreatedBySDK=!1,this.adMediaContainer=null,this.adPlayer=null,this.adContainer=null,this.companionElement=null,this.staticQuartilesFired=new Set,this.sequencer=null,this.pendingAdError=null,this.preRollHoldActive=!1,this.preRollHoldHandler=null,this.breakContentGateHandler=null,this.destroyed=!1,this.abortingBreak=!1,this.overlayImageEl=null,this.imageAssetTimer=null,this.chainResolver=null,this.transitionPhaseReporter=null,this.chaining=!1,this.diagnoseHandler=null,this.playerContainerOriginalStyle="",this.companionRemoveTimer=null,this.adFadeTimer=null,this.syncing=!1,this.contentVolumeHandler=null,this.adVolumeChangeHandler=null,this.volumeChangeCallback=null,this.stage=null,this.resizeObserver=null,this.contentVideoElement=null,this.contentVideoGeometryHandler=null,this.contentVideoMutationObserver=null,this.resumeGeneration=0,this.singleSurfaceDevice=!1,this.currentSelectedFormat=void 0,this.audioFocus=null,this.state=m(),this.state.playerContainer=r??null,this.clock=T,this.singleSurfaceDevice=S,this.contentPlayer=e,this.adBreakCutSafetyMarginSec=h,this.doubleBoxAudio=u,this.container=t,r&&(this.playerContainer=r),this.createAdAdapter=i,this.debug=n,this.state.gamConfig=o,this.preloadMode=d,this.insertionMode=l,this.pauseAdPresenter=new P({contentPlayer:this.contentPlayer,getStage:()=>this.stage,resolveUri:e=>this.resolveUri(e),diagnose:(e,t,r)=>this.diagnose(e,t,r),log:(...e)=>this.log(...e)}),this.assetParameters=new y(this.state,{contentPlayer:this.contentPlayer,container:this.container,getGamStreamManager:()=>this.state.gamStreamManager,getCurrentBreak:()=>this.currentBreak,getEffectiveBreakDuration:()=>this.effectiveBreakDurationSec,diagnose:(e,t,r)=>this.diagnose(e,t,r),log:(...e)=>this.log(...e)}),this.gamPodBridge=new p(this.state,{contentPlayer:this.contentPlayer,debug:this.debug,getAdPlayer:()=>this.adPlayer,getAdContainer:()=>this.adContainer,isSharedElement:()=>"shared-element"===this.effectiveInsertionMode(),getCurrentBreak:()=>this.currentBreak,getCurrentAsset:()=>this.currentAsset,getEffectiveBreakDuration:()=>this.effectiveBreakDurationSec,getAdPlayerStreamRemainingSec:()=>this.adPlayerStreamRemainingSec(),emitStep:e=>this.emitStep(e),emitAdStart:(e,t,r,s,a)=>this.callbacks?.onAdStart(e,t,r,s,a),emitAdEnd:(e,t,r,s,a)=>this.callbacks?.onAdEnd(e,t,r,s,a),onAdEnded:()=>this.onAdEnded(),pushAssetParameters:(e,t)=>this.pushAssetParameters(e,t),resolveSessionAdTagParameters:()=>this.assetParameters.resolveSessionAdTagParameters(),diagnose:(e,t,r)=>this.diagnose(e,t,r),log:(...e)=>this.log(...e)}),this.adStage=new A({contentPlayer:this.contentPlayer,container:this.container,getPlayerContainer:()=>this.playerContainer,setPlayerContainer:e=>{this.playerContainer=e},setPlayerContainerOriginalStyle:e=>{this.playerContainerOriginalStyle=e},onPlayerContainerCreated:()=>{this.playerContainerCreatedBySDK=!0},getStage:()=>this.stage,setStage:e=>{this.stage=e},getContentVideo:()=>this.contentVideoElement,setContentVideo:e=>{this.contentVideoElement=e},getAdContainer:()=>this.adContainer,setAdContainer:e=>{this.adContainer=e},getAdMediaContainer:()=>this.adMediaContainer,setAdMediaContainer:e=>{this.adMediaContainer=e},setAdPlayer:e=>{g(this.state),this.adPlayer=e},createAdAdapter:e=>this.createAdAdapter(e),setupEventListeners:()=>this.setupEventListeners(),resizeVast:()=>this.vastAdManager?.resize(),getVariant:e=>this.getVariant(e),getCurrentBreak:()=>this.currentBreak,resolveUri:e=>this.resolveUri(e),getCompanion:()=>this.companionElement,setCompanion:e=>{this.companionElement=e},getOverlayImage:()=>this.overlayImageEl,setOverlayImage:e=>{this.overlayImageEl=e},getImageAssetTimer:()=>this.imageAssetTimer,setImageAssetTimer:e=>{this.imageAssetTimer=e},isChaining:()=>this.chaining,log:(...e)=>this.log(...e)}),this.preloader=new v({state:this.state,clock:this.clock,adStartBudgetMs:AdPlayerController.AD_START_BUDGET_MS,gamPodAdStartBudgetMs:AdPlayerController.GAM_POD_AD_START_BUDGET_MS,minAdStartBudgetMs:AdPlayerController.MIN_AD_START_BUDGET_MS,debug:this.debug,isPlaying:()=>this.isPlaying,preloadMode:this.preloadMode,effectiveInsertionMode:()=>this.effectiveInsertionMode(),getVariant:e=>this.getVariant(e),getAssets:e=>this.getAssets(e),resolveUri:e=>this.resolveUri(e),resolveAssetUri:(e,t)=>this.resolveAssetUri(e,t),getCompanionVariant:e=>this.getVariant(e),getAdPlayer:()=>this.adPlayer,getAdContainer:()=>this.adContainer,buildVastTagUrl:(e,t)=>this.buildVastTagUrl(e,t),resolveChainedSuccessor:e=>this.resolveChainedSuccessor(e),show:()=>this.show(),reapplyAdContainerLayout:e=>this.reapplyAdContainerLayout(e),getCurrentBreak:()=>this.currentBreak,onBreakVisible:e=>this.callbacks?.onBreakVisible?.(e),onAdError:e=>this.onAdError(e),diagnose:(e,t,r)=>this.diagnose(e,t,r),log:(...e)=>this.log(...e)}),this.presentationModes=new C(this.state,{contentPlayer:this.contentPlayer,getAdPlayer:()=>this.adPlayer,insertionMode:this.insertionMode,getCurrentBreak:()=>this.currentBreak,getCurrentFormat:()=>this.currentSelectedFormat,isSharedElementActive:()=>this.state.sharedAdClaimActive,getVariant:e=>this.getVariant(e),isAdPlaying:()=>this.isAdPlaying(),hasCurrentAsset:()=>null!==this.currentAsset,contentSourceIsReloadable:()=>null!==this.sharedElementPlayer.restorableContentSource(),adoptPictureInPictureMidBreak:()=>this.adoptPictureInPictureMidBreak(),restoreLayout:e=>this.restoreLayout(e),applyLayout:e=>this.applyLayout(e),show:()=>this.show(),playAdThroughContentElement:()=>this.sharedElementPlayer.playAdThroughContentElement(),getContentPositionAtBreakStart:()=>this.state.contentPositionAtBreakStart,setContentPositionAtBreakStart:e=>this.resumeCoordinator.reanchorMidBreak(e),gateContentForBreak:()=>this.gateContentForBreak(),diagnose:(e,t,r)=>this.diagnose(e,t,r),log:(...e)=>this.log(...e)}),this.resumeCoordinator=new f(this.state,{contentPlayer:this.contentPlayer,clock:this.clock,getInsertionMode:()=>this.effectiveInsertionMode(),gateContentForBreak:()=>this.gateContentForBreak(),clearContentGate:e=>this.clearContentGate(e),diagnose:(e,t,r)=>this.diagnose(e,t,r),log:(...e)=>this.log(...e)}),this.recoveryDeps={contentPlayer:e,getCallbacks:()=>this.callbacks,preloadMode:d,contentRecovery:M,resumeCoordinator:this.resumeCoordinator,diagnose:(e,t,r)=>this.diagnose(e,t,r),log:(...e)=>this.log(...e)},this.sharedElementPlayer=new k(this.state,{contentPlayer:this.contentPlayer,getAdPlayer:()=>this.adPlayer,resolveAssetUri:(e,t)=>this.resolveAssetUri(e,t),liveEdgeDistanceSec:()=>this.resumeCoordinator.liveEdgeDistanceSec(),rebindContentElementIfSwapped:()=>this.rebindContentElementIfSwapped(),createAdAdapter:(e,t)=>this.createAdAdapter(e,t),reportAdProgress:e=>this.reportAdProgress(e),processTimedMetadata:e=>this.gamPodBridge.processTimedMetadata(e),diagnose:(e,t,r)=>this.diagnose(e,t,r),log:(...e)=>this.log(...e),clearContentGate:()=>this.clearContentGate(),hide:()=>this.hide(),waitForLiveEdge:e=>this.resumeCoordinator.waitForLiveEdge(e),getVariant:e=>this.getVariant(e),getAssets:e=>this.getAssets(e)}),this.variantSelector=new b(this.state,{getEffectiveInsertionMode:()=>this.effectiveInsertionMode(),singleSurfaceDevice:this.singleSurfaceDevice,preloadMode:this.preloadMode,getIsContentPictureInPicture:()=>this.isContentPictureInPicture(),getIsGamReady:()=>this.isGamReady(),getSupportsParallelBuffering:()=>this.contentPlayer.supportsParallelBuffering,diagnose:(e,t,r)=>this.diagnose(e,t,r)}),this.assetPlayback=new B(this.state,{getAdPlayer:()=>this.adPlayer,getAdContainer:()=>this.adContainer,getAdMediaContainer:()=>this.adMediaContainer,clearStaticQuartiles:()=>this.staticQuartilesFired.clear(),getVariant:e=>this.getVariant(e),getGamEventCallbacks:()=>this.gamEventCallbacks,getSequencer:()=>this.sequencer,debug:this.debug,resolveUri:e=>this.resolveUri(e),buildGamUrl:(e,t)=>this.buildGamUrl(e,t),buildVastTagUrl:(e,t)=>this.buildVastTagUrl(e,t),onAdError:e=>this.onAdError(e),onAdEnded:()=>this.onAdEnded(),drive:e=>this.drive(e),clearOverlayImage:()=>this.clearOverlayImage(),revealFirstFrame:()=>this.preloader.revealFirstFrame(),transferPictureInPictureToAd:()=>this.presentationModes.transferPictureInPictureToAd(),isGamPodAsset:e=>this.isGamPodAsset(e),hasGamIdentityMismatch:e=>this.gamPodBridge.hasIdentityMismatch(e),beginGamPodTracking:e=>this.gamPodBridge.beginGamPodTracking(e),recordAdProgress:e=>this.adPlayerEvents.recordAdProgress(e),reportPhase:e=>this.reportPhase(e),armAdStartBudget:e=>this.armAdStartBudget(e),remainingAdStartBudgetMs:()=>this.remainingAdStartBudgetMs(),loadWithinBudget:(e,t)=>this.loadWithinBudget(e,t),waitForFirstFrame:()=>this.waitForFirstFrame(),getOverlayImage:()=>this.overlayImageEl,setOverlayImage:e=>{this.overlayImageEl=e},getImageAssetTimer:()=>this.imageAssetTimer,setImageAssetTimer:e=>{this.imageAssetTimer=e},getVastPodPosition:()=>this.vastPodPosition,setVastPodPosition:e=>{this.vastPodPosition=e},diagnose:(e,t,r)=>this.diagnose(e,t,r),log:(...e)=>this.log(...e)}),this.breakSequenceDriver=new E(this.state,{getChainResolver:()=>this.chainResolver,getEffectiveInsertionMode:()=>this.effectiveInsertionMode(),getVastPodPosition:()=>this.vastPodPosition,setVastPodPosition:e=>{this.vastPodPosition=e},getPendingAdError:()=>this.pendingAdError,setPendingAdError:e=>{this.pendingAdError=e},getChaining:()=>this.chaining,setChaining:e=>{this.chaining=e},isExpandingPodAsset:e=>this.isExpandingPodAsset(e),fallBackToPodLevelReporting:()=>this.fallBackToPodLevelReporting(),resetGamPodTracking:()=>this.resetGamPodTracking(),emitGamPodSteps:e=>this.gamPodBridge.emitGamPodSteps(e),playAsset:(e,t,r)=>this.playAsset(e,t,r),endBreak:()=>this.endBreak(),playBreak:(e,t,r)=>this.playBreak(e,t,r),diagnose:(e,t,r)=>this.diagnose(e,t,r),log:(...e)=>this.log(...e)}),this.adPlayerEvents=new F(this.state,{getAdPlayer:()=>this.adPlayer,getContentPlayer:()=>this.contentPlayer,getGamEventCallbacks:()=>this.gamEventCallbacks,getSequencer:()=>this.sequencer,getAudioFocus:()=>this.audioFocus,getSyncing:()=>this.syncing,setSyncing:e=>{this.syncing=e},setAdVolumeChangeHandler:e=>{this.adVolumeChangeHandler=e},setContentVolumeHandler:e=>{this.contentVolumeHandler=e},notifyVolumeChange:()=>this.notifyVolumeChange(),onAdEnded:()=>this.onAdEnded(),processTimedMetadata:e=>this.gamPodBridge.processTimedMetadata(e),toAdError:e=>this.toAdError(e),revealFirstFrame:()=>this.revealFirstFrame(),clearFirstFrameResolver:()=>this.preloader.clearFirstFrameResolver(),drive:e=>this.drive(e),setPendingAdError:e=>{this.pendingAdError=e},log:(...e)=>this.log(...e),maybePrepositionContentForResume:()=>"single-decoder"!==this.preloadMode&&this.resumeCoordinator.maybePrepositionContentForResume(),maybeCatchUpContentForResume:e=>"single-decoder"!==this.preloadMode&&this.resumeCoordinator.maybeCatchUpContentForResume(e),getStaticQuartilesFired:()=>this.staticQuartilesFired,onGamPodAdProgress:e=>this.gamPodBridge.onGamPodAdProgress(e)})}setGamEventCallbacks(e){this.gamPodBridge.setEventCallbacks(e)}setDiagnoseHandler(e){this.diagnoseHandler=e}diagnose(e,t,r){this.diagnoseHandler?.(e,t,r)}holdContentForPreRoll(e){if(this.preRollHoldActive)return;const t=this.getVariant(e);t&&this.shouldPauseContent(t.format)&&(this.preRollHoldActive=!0,this.contentPlayer.pause(),this.preRollHoldHandler=()=>{this.preRollHoldActive&&this.contentPlayer.pause()},this.contentPlayer.on("playing",this.preRollHoldHandler),this.diagnose("DA-PREROLL-CONTENT-HELD","Content held at session start until the pending pre-roll begins.",{breakId:e.id}))}releaseContentHold(){this.preRollHoldActive&&(this.preRollHoldActive=!1,this.preRollHoldHandler&&(this.contentPlayer.off("playing",this.preRollHoldHandler),this.preRollHoldHandler=null))}gateContentForBreak(){this.contentPlayer.pause(),this.breakContentGateHandler||(this.breakContentGateHandler=()=>{this.contentPaused&&!this.state.resumeCatchupActive&&this.contentPlayer.pause()},this.contentPlayer.on("playing",this.breakContentGateHandler))}clearContentGate(e=!1){this.resumeCoordinator.endResumeCatchup(e),this.breakContentGateHandler&&(this.contentPlayer.off("playing",this.breakContentGateHandler),this.breakContentGateHandler=null)}setChainResolver(e){this.chainResolver=e}setResumeCueAnchorResolver(e){this.resumeCoordinator.setCueAnchorResolver(e)}setResumeCueEndResolver(e){this.resumeCoordinator.setCueEndResolver(e)}ingestStreamClockCue(e){this.resumeCoordinator.ingestStreamClockCue(e)}setTransitionPhaseReporter(e){this.transitionPhaseReporter=e}reportPhase(e){this.transitionPhaseReporter?.(e)}setBreakResumePolicy(e,t,r=!1){this.adInsertionType=e,this.breakTimebase=t,this.state.continueContentDuringBreak=r,this.state.continueRefusalReported=!1}initialize(){this.adContainer||(this.setupOverlay(),this.presentationModes.setupPictureInPictureTracking(),"adaptive"===this.insertionMode&&this.presentationModes.setupFullscreenTracking())}async startGamSession(e,t){await this.gamPodBridge.startGamSession(e,t)}endGamSession(){this.gamPodBridge.endGamSession()}setSessionAssetParameters(e){this.assetParameters.setSessionAssetParameters(e)}setManifestAssetParameters(e){this.assetParameters.setManifestAssetParameters(e)}updateAssetParameters(e){this.assetParameters.updateAssetParameters(e)}resolveExternalVendorConfiguration(e){return this.assetParameters.resolveExternalVendorConfiguration(e)}updateAssetParameterMacros(e){this.assetParameters.updateAssetParameterMacros(e)}get updatedAssetParameters(){return this.state.updatedAssetParameters}setSessionAssetParameterMacros(e){this.assetParameters.setSessionAssetParameterMacros(e)}get sessionAssetParameterMacros(){return this.assetParameters.getSessionAssetParameterMacros()}buildVastTagUrl(e,t){return this.assetParameters.buildVastTagUrl(e,t)}pushAssetParameters(e,t){this.assetParameters.pushAssetParameters(e,t)}setupOverlay(){this.adStage.setupOverlay()}isGamPodAsset(e){return this.gamPodBridge.isGamPodAsset(e)}isExpandingPodAsset(e){return this.gamPodBridge.isExpandingPodAsset(e)}fallBackToPodLevelReporting(){this.gamPodBridge.fallBackToPodLevelReporting()}resetGamPodTracking(){this.gamPodBridge.resetGamPodTracking()}emitGamPodSteps(e){this.gamPodBridge.emitGamPodSteps(e)}isGamEnabled(){return this.gamPodBridge.isGamEnabled()}isGamReady(){return this.gamPodBridge.isGamReady()}isOverlayInsertion(){return"overlay"===this.effectiveInsertionMode()}settleAssetProgress(){this.adPlayerEvents.settleAssetProgress()}reportAdProgress(e){this.adPlayerEvents.reportAdProgress(e)}setupEventListeners(){this.adPlayerEvents.setupEventListeners()}async preload(e){return this.preloader.preload(e)}renderLshapeContentBackdrop(e){this.adStage.renderLshapeContentBackdrop(e)}currentInsertionMode(){return this.presentationModes.currentInsertionMode()}effectiveInsertionMode(){return this.presentationModes.effectiveInsertionMode()}isContentPictureInPicture(){return this.presentationModes.isPictureInPicture()}rebindContentElementIfSwapped(){this.presentationModes.rebindContentElementIfSwapped()}teardownFullscreenTracking(){this.presentationModes.teardownFullscreenTracking()}teardownPictureInPictureTracking(){this.presentationModes.teardownPictureInPictureTracking()}isPictureInPicture(){return this.presentationModes.isPictureInPicture()}pictureInPictureOwner(){return this.presentationModes.pictureInPictureOwner()}isShowingAd(){if(!this.isAdPlaying())return!1;const e=this.currentDeclaredFormat();return!n(e,this.isContentPictureInPicture())}currentDeclaredFormat(){return this.currentSelectedFormat??(this.currentBreak?l(this.currentBreak):void 0)}async playBreak(t,s,a){if(this.state.preloadGeneration++,this.resumeGeneration++,!this.sharedElementPlayer.beginSharedActivity())return;this.releaseContentHold(),this.rebindContentElementIfSwapped();const n=a?.effectiveDurationSec??null;this.effectiveBreakDurationSec=null!==n&&n>0?n:null;const d=this.effectiveBreakDurationSec??t.duration;if("shared-element"===this.effectiveInsertionMode())return g(this.state),void await this.sharedElementPlayer.playSharedElementBreak(t,s);if(!this.adPlayer)return void this.log("playBreak called before initialize()");const l=this.getVariant(t);if(!l)return void this.log("No valid variant found for break:",t.id);this.currentBreak=t,this.callbacks=s,this.isPlaying=!0,this.firstFrameGateActive=a?.firstFrameGate??!1,this.firstFrameGateShown=!1;const h=this.continueContentDuringBreakReason(l.format),u=r(t),c=this.shouldPauseContent(l.format)&&"ok"===h&&!u;c||u||this.state.continueRefusalReported||!this.shouldPauseContent(l.format)||(this.state.continueRefusalReported=!0,this.diagnose("DA-CONTINUE-PLAYOUT-REFUSED",`Content will be paused for breaks this session: ${h}`,{reason:h,format:l.format})),this.audioFocus=l.format===e.DOUBLE?this.doubleBoxAudio:c?"ad":null,this.adPlayer&&("content"===this.audioFocus?(this.adPlayer.muted=!0,this.adPlayer.volume=0):(this.adPlayer.muted=this.contentPlayer.muted,this.adPlayer.volume=this.contentPlayer.volume,"ad"===this.audioFocus&&(this.contentPlayer.muted=!0)));const m=this.declaredFormatOf(t);if(this.currentSelectedFormat=m,this.state.pipAdoptedForBreak=i(m,this.isContentPictureInPicture()),this.state.pipAdoptedForBreak&&this.diagnose("DA-PIP-FORMAT-OVERRIDE",`Picture-in-picture: ${m} break plays as single`,{declared:m,effective:e.SINGLE,reason:"picture-in-picture",midBreak:!1}),this.shouldPauseContent(l.format)?(this.resumeCoordinator.captureBreakAnchor(t),c?(this.contentPaused=!1,this.resumeCoordinator.beginContinueContentPlayout()):(this.contentPaused=!0,this.gateContentForBreak())):this.contentPaused=!1,this.applyLayout(l),l.format===e.LSHAPE_CONTENT)return this.renderLshapeContentBackdrop(l),void(d&&d>0?(this.log("Starting duration timer:",d,"seconds"),this.durationTimer=setTimeout(()=>{this.log("Duration timer expired, ending break"),this.endBreak()},1e3*d)):this.endBreak());this.firstFrameGateActive||(this.show(),this.reapplyAdContainerLayout(l));if(0===this.getAssets(t).length)return this.log("No assets in break, ending immediately"),void this.endBreak();{const e=Math.max(0,d||0)+this.adBreakCutSafetyMarginSec;T(this.state,e,()=>{this.log("Break-cut timer expired, ending break"),this.firstFrameRejector?this.onAdError(new R("Break cut before first frame rendered","DA-AD-START-TIMEOUT")):this.sequencer&&this.currentBreak&&this.isPlaying?(this.callbacks?.onBreakCut?.(this.currentBreak),this.drive(this.sequencer.onOutcome({kind:"break-elapsed"}))):this.endBreak()},()=>this.onAdError(new R("Ad playback startup timed out","DA-AD-START-TIMEOUT")))}this.sequencer=new o("overlay",()=>null,this.selectionInputs()),await this.drive(this.sequencer.beginBreak(t)),this.preloadChainedSuccessor(t.id)}get resumeHandoverInMotion(){return this.state.resumeCatchupActive}reapplyAdContainerLayout(e){this.adStage.reapplyAdContainerLayout(e)}adoptPictureInPictureMidBreak(){this.presentationModes.adoptPictureInPictureMidBreak()}async restorePictureInPictureToContent(){await this.presentationModes.restorePictureInPictureToContent()}continueContentDuringBreakReason(e){return this.variantSelector.continueContentDuringBreakReason(e)}shouldPauseContent(t){return t===e.SINGLE||t===e.LSHAPE_AD}selectionInputs(){return this.variantSelector.selectionInputs()}breakNothingToPlayReason(e){return this.variantSelector.breakNothingToPlayReason(e)}unknownVariantFormatsOf(e){return this.variantSelector.unknownVariantFormatsOf(e)}diagnoseUnknownVariantFormats(e){this.variantSelector.diagnoseUnknownVariantFormats(e)}declaredFormatOf(e){const{selected:t,deviceQualified:r}=h(e,this.selectionInputs());return(t??r)?.format??l(e)}getVariant(e){return this.variantSelector.getVariant(e)}applyLayout(e){this.adStage.applyLayout(e)}restoreLayout(e){this.adStage.restoreLayout(e)}getAssets(e){return d(this.getVariant(e))}async playAsset(e,t,r){return this.assetPlayback.playAsset(e,t,r)}clearOverlayImage(){this.adStage.clearOverlayImage()}async showPauseImage(e){return this.pauseAdPresenter.showPauseImage(e)}hidePauseImage(){this.pauseAdPresenter.hidePauseImage()}async drive(e){return this.breakSequenceDriver.drive(e)}emitStep(e){this.breakSequenceDriver.emitStep(e)}async resolveAssetUri(e,t){return this.assetPlayback.resolveAssetUri(e,t)}async buildGamUrl(e,t){return this.gamPodBridge.buildGamUrl(e,t)}resolveUri(e){return this.assetParameters.resolveUri(e)}adPlayerStreamRemainingSec(){return this.assetPlayback.adPlayerStreamRemainingSec()}onAdEnded(){this.currentBreak&&this.isPlaying&&this.sequencer&&(this.settleAssetProgress(),this.drive(this.sequencer.onOutcome({kind:"asset-ended"})))}toAdError(e){return this.assetPlayback.toAdError(e)}onAdError(e){this.adPlayerEvents.onAdError(e)}endBreak(){if(!this.currentBreak)return;this.log("Break ended:",this.currentBreak.id),M(this.state),this.state.gamPodTracker&&this.emitGamPodSteps(this.state.gamPodTracker.finish()),this.resetGamPodTracking(),this.pushAssetParameters(void 0);const e=this.currentBreak,s=this.getVariant(e)?.format;let a=this.abortingBreak?null:this.resolveChainedSuccessor(e.id);if(a){const e=this.breakNothingToPlayReason(a);this.diagnoseUnknownVariantFormats(a),e&&(this.diagnose("DA-BREAK-NO-PLAYABLE-ASSET",`chained break ${a.id} has no playable variant (${e}); it will not begin.`,{breakId:a.id,reason:e}),this.callbacks?.onAdError(a,void 0,new R(`Break ${a.id} has no playable variant (${e})`,"DA-BREAK-NO-PLAYABLE-ASSET")),this.callbacks?.onChainedBreakSkipped?.(a),a=null)}if(a)return void this.advanceToChainedBreak(e,a);if(this.currentSelectedFormat=void 0,this.resumeCoordinator.endContinueContentPlayout(),this.restorePictureInPictureToContent(),this.isPlaying=!1,this.currentBreak=null,this.resumeGeneration++,this.effectiveBreakDurationSec=null,g(this.state),this.preloader.clearWarmedBreaks(),this.firstFrameGateActive=!1,this.firstFrameGateShown=!1,this.clearFirstFrame("Break ended before first frame"),"ad"===this.audioFocus&&this.adPlayer?(this.contentPlayer.muted=this.adPlayer.muted,this.contentPlayer.volume=this.adPlayer.volume):"content"===this.audioFocus&&this.adPlayer&&(this.adPlayer.muted=this.contentPlayer.muted,this.adPlayer.volume=this.contentPlayer.volume),this.audioFocus=null,this.clearOverlayImage(),this.vastAdManager?.reset(),this.hide(),this.restoreLayout(s),this.adPlayer?.pause(),this.state.sharedAdClaimActive){const t=this.callbacks;return this.sharedElementPlayer.detachSharedAdListeners(),this.abortingBreak=!1,this.state.earlyReturnResumeOffsetSec=null,this.sharedElementPlayer.restoreSharedElementContent(e),void t?.onBreakEnd(e)}const i="recreate"===this.recoveryDeps.contentRecovery&&"function"==typeof this.contentPlayer.recreateMediaPipeline,n="single-decoder"===this.preloadMode||i?this.adPlayer?.unload?.():void 0;if(this.contentPaused&&r(e))this.contentPaused=!1,this.clearContentGate();else if(this.contentPaused){this.contentPaused=!1,this.clearContentGate(this.resumeHandoverInMotion);const r=this.resumeGeneration;S(this.recoveryDeps,{breakInfo:e,earlyReturnOffsetSec:this.state.earlyReturnResumeOffsetSec,isLiveResume:this.breakTimebase===t.WALLCLOCK,released:n,isCurrent:()=>r===this.resumeGeneration})}this.abortingBreak=!1,this.state.earlyReturnResumeOffsetSec=null,this.callbacks?.onBreakEnd(e)}currentAdAsset(){return this.isPlaying?this.currentAsset:null}skipBreak(){this.isPlaying&&(this.log("Skipping break:",this.currentBreak?.id),M(this.state),this.sequencer&&this.currentBreak?this.drive(this.sequencer.onOutcome({kind:"break-elapsed"})):this.endBreak())}abortBreak(e){this.isPlaying&&(this.log("Early return — break removed from manifest:",this.currentBreak?.id),this.state.earlyReturnResumeOffsetSec=Math.max(0,e),this.abortingBreak=!0,M(this.state),this.sequencer&&this.currentBreak?this.drive(this.sequencer.onOutcome({kind:"break-elapsed"})):this.endBreak())}stopActiveBreak(){g(this.state),this.sharedElementPlayer.cancelSharedRestore()&&this.isPlaying&&(this.log("Aborting active break on session end:",this.currentBreak?.id),M(this.state),this.sharedElementPlayer.detachSharedAdListeners(),this.sharedElementPlayer.releaseContentElementAdPlayer(),this.adPlayer?.pause(),this.clearOverlayImage(),this.vastAdManager?.reset(),this.hide(),this.restoreLayout(this.currentBreak?this.getVariant(this.currentBreak)?.format??void 0:void 0),this.isPlaying=!1,this.currentBreak=null,this.currentAsset=null,this.contentPaused=!1,this.clearContentGate(),this.resumeCoordinator.endContinueContentPlayout(),this.effectiveBreakDurationSec=null,this.firstFrameGateActive=!1,this.firstFrameGateShown=!1,this.clearFirstFrame("Break aborted before first frame"),this.audioFocus=null)}resolveChainedSuccessor(e){return this.breakSequenceDriver.resolveChainedSuccessor(e)}advanceToChainedBreak(e,t){this.breakSequenceDriver.advanceToChainedBreak(e,t)}preloadChainedSuccessor(e){this.preloader.preloadChainedSuccessor(e)}revealFirstFrame(){this.assetPlayback.revealFirstFrame()}armAdStartBudget(e){this.preloader.armAdStartBudget(e)}remainingAdStartBudgetMs(){return this.preloader.remainingAdStartBudgetMs()}loadWithinBudget(e,t){return this.preloader.loadWithinBudget(e,t)}async waitForFirstFrame(){return this.preloader.waitForFirstFrame()}clearFirstFrame(e="Break ended before first frame"){this.preloader.clearFirstFrame(e)}show(){this.adStage.show()}hide(){this.adStage.hide()}isAdPlaying(){return this.isPlaying}get muted(){return"content"===this.audioFocus?this.contentPlayer.muted:"ad"===this.audioFocus?this.adPlayer?this.adPlayer.muted:this.contentPlayer.muted:this.isPlaying&&this.adPlayer?this.adPlayer.muted:this.contentPlayer.muted}set muted(e){if("content"===this.audioFocus)this.contentPlayer.muted=e;else if("ad"===this.audioFocus)this.adPlayer&&(this.adPlayer.muted=e);else{(this.isPlaying&&this.adPlayer?this.adPlayer:this.contentPlayer).muted=e}this.vastAdManager?.setMuted(e),this.notifyVolumeChange()}get volume(){return"content"===this.audioFocus?this.contentPlayer.volume:"ad"===this.audioFocus?this.adPlayer?this.adPlayer.volume:this.contentPlayer.volume:this.isPlaying&&this.adPlayer?this.adPlayer.volume:this.contentPlayer.volume}set volume(e){if("content"===this.audioFocus)this.contentPlayer.volume=e;else if("ad"===this.audioFocus)this.adPlayer&&(this.adPlayer.volume=e);else{(this.isPlaying&&this.adPlayer?this.adPlayer:this.contentPlayer).volume=e}this.vastAdManager?.setVolume(e),this.notifyVolumeChange()}setVolumeChangeCallback(e){this.volumeChangeCallback=e}notifyVolumeChange(){this.volumeChangeCallback?.(this.muted,this.volume)}destroy(){if(this.destroyed)return;if(this.resumeGeneration++,g(this.state),!this.sharedElementPlayer.beginSharedActivity())return;this.destroyed=!0,this.adStage.destroy(),this.resumeCoordinator.cancelResumeLandedProbe(),this.resumeCoordinator.dispose(),this.resumeCoordinator.endResumeCatchup(),this.resumeCoordinator.endContinueContentPlayout(),this.state.resumePrepositionSettle&&(clearTimeout(this.state.resumePrepositionSettle),this.state.resumePrepositionSettle=null),this.releaseContentHold(),this.clearContentGate(),this.clearFirstFrame("Controller destroyed before first frame"),this.firstFrameGateActive=!1,this.firstFrameGateShown=!1,this.resetGamPodTracking(),M(this.state),this.companionRemoveTimer&&(clearTimeout(this.companionRemoveTimer),this.companionRemoveTimer=null),this.adFadeTimer&&(clearTimeout(this.adFadeTimer),this.adFadeTimer=null),this.adPlayer?.pause(),this.clearOverlayImage(),this.pauseAdPresenter.destroy(),this.contentVolumeHandler&&(this.contentPlayer.off("volumechange",this.contentVolumeHandler),this.contentVolumeHandler=null),this.sharedElementPlayer.detachSharedAdListeners(),this.sharedElementPlayer.releaseContentElementAdPlayer(),this.state.pipTransferredToAd=!1,this.teardownFullscreenTracking(),this.teardownPictureInPictureTracking(),this.adVolumeChangeHandler&&this.adPlayer&&this.adPlayer.off("volumechange",this.adVolumeChangeHandler),this.adVolumeChangeHandler=null,this.hide(),this.adStage.destroy(),this.resizeObserver?.disconnect(),this.resizeObserver=null,this.contentVideoMutationObserver?.disconnect(),this.contentVideoMutationObserver=null,this.contentVideoElement&&this.contentVideoGeometryHandler&&(this.contentVideoElement.removeEventListener("loadedmetadata",this.contentVideoGeometryHandler),this.contentVideoElement.removeEventListener("resize",this.contentVideoGeometryHandler)),this.contentVideoElement=null,this.contentVideoGeometryHandler=null;const e=this.adStage.restoreContent(this.playerContainerCreatedBySDK,this.playerContainerOriginalStyle);this.companionElement&&this.companionElement.parentElement&&(this.companionElement.parentElement.removeChild(this.companionElement),this.companionElement=null),this.isPlaying=!1,this.currentBreak=null,this.adContainer&&this.adContainer.parentElement&&this.adContainer.parentElement.removeChild(this.adContainer),!e&&this.stage&&this.stage.parentElement&&this.stage.parentElement.removeChild(this.stage),this.stage=null,this.adContainer=null,this.adMediaContainer=null;try{this.adPlayer?.destroy()}catch(e){this.log("Ad player adapter destroy failed (ignored):",e)}this.adPlayer=null,this.callbacks=null,this.gamPodBridge.destroy(),this.vastAdManager?.destroy(),this.vastAdManager=null,this.log("Ad player destroyed")}log(...e){this.debug&&u("[OptiViewAds:AdPlayerController]",...e)}}AdPlayerController.AD_START_BUDGET_MS=5e3,AdPlayerController.GAM_POD_AD_START_BUDGET_MS=1e4,AdPlayerController.MIN_AD_START_BUDGET_MS=3e3;
1
+ import{BreakFormat as e,Timebase as t,isPostRollBreak as r}from"../types/BreakManifest";import{DEFAULT_AD_BREAK_CUT_SAFETY_MARGIN_SEC as s,DEFAULT_DOUBLE_BOX_AUDIO as a}from"../types/OptiViewAdsConfig";import{pipOverridesFormat as i,pipSuppressesBackdrop as n}from"./pictureInPicture";import{AdBreakSequencer as o,assetsOfVariant as d,resolveBreakFormat as h}from"./AdBreakSequencer";import{selectVariantDetailed as l}from"./selectVariant";import{debugLog as u}from"../diagnostics/debugLog";import{systemClock as c}from"./BreakScheduler";import{createAdPlayerState as m,invalidateMediaPreload as g}from"./adPlayer/AdPlayerState";import{PauseAdPresenter as P}from"./adPlayer/PauseAdPresenter";import{AssetParameterResolver as p}from"./adPlayer/AssetParameterResolver";import{GamPodBridge as y}from"./adPlayer/GamPodBridge";import{AdStage as A}from"./adPlayer/AdStage";import{createDomPresentationHost as C,toPresentationSurfaces as v}from"./adPlayer/presentation/DomPresentationHost";import{AdPreloader as k}from"./adPlayer/AdPreloader";import{PresentationModeTracker as f}from"./adPlayer/PresentationModeTracker";import{SharedElementBreakPlayer as S}from"./adPlayer/SharedElementBreakPlayer";import{ContentResumeCoordinator as B}from"./adPlayer/ContentResumeCoordinator";import{resumeContentAfterBreak as b}from"./adPlayer/ContentRecovery";import{AssetPlayback as E}from"./adPlayer/AssetPlayback";import{SurfaceRevealReporter as T}from"./adPlayer/SurfaceRevealReporter";import{BreakSequenceDriver as M}from"./adPlayer/BreakSequenceDriver";import{VariantSelector as R}from"./adPlayer/VariantSelector";import{AdPlayerEvents as F}from"./adPlayer/AdPlayerEvents";import{AudioController as I}from"./adPlayer/AudioController";import{armPlaybackCutoff as G,clearPlaybackCutoff as w}from"./adPlayer/PlaybackCutoff";import{AdError as D}from"../types/AdError";import{SuccessorQueue as V}from"./adPlayer/SuccessorQueue";export class AdPlayerController{get contentPaused(){return this.state.contentPaused}set contentPaused(e){this.state.contentPaused=e}get firstFrameGateActive(){return this.state.firstFrameGateActive}set firstFrameGateActive(e){this.state.firstFrameGateActive=e}get firstFrameGateShown(){return this.state.firstFrameGateShown}set firstFrameGateShown(e){this.state.firstFrameGateShown=e}get firstFrameRejector(){return this.state.firstFrameRejector}set firstFrameRejector(e){this.state.firstFrameRejector=e}get gamEventCallbacks(){return this.gamPodBridge.eventCallbacks}get vastAdManager(){return this.state.vastAdManager}set vastAdManager(e){this.state.vastAdManager=e}get currentBreak(){return this.state.currentBreak}set currentBreak(e){this.state.currentBreak=e}get currentAsset(){return this.state.currentAsset}set currentAsset(e){this.state.currentAsset=e}get callbacks(){return this.state.callbacks}set callbacks(e){this.state.callbacks=e}get isPlaying(){return this.state.isPlaying}set isPlaying(e){this.state.isPlaying=e}get effectiveBreakDurationSec(){return this.state.effectiveBreakDurationSec}set effectiveBreakDurationSec(e){this.state.effectiveBreakDurationSec=e}get adBreakCutSafetyMarginSec(){return this.state.adBreakCutSafetyMarginSec}set adBreakCutSafetyMarginSec(e){this.state.adBreakCutSafetyMarginSec=e}get durationTimer(){return this.state.durationTimer}set durationTimer(e){this.state.durationTimer=e}get adInsertionType(){return this.state.adInsertionType}set adInsertionType(e){this.state.adInsertionType=e}get breakTimebase(){return this.state.breakTimebase}set breakTimebase(e){this.state.breakTimebase=e}constructor(e,t,r,i,n=!1,o,d="parallel",h="overlay",l=s,u=a,b=!1,G=c,w="none"){this.playerContainerCreatedBySDK=!1,this.adMediaContainer=null,this.adPlayer=null,this.surfaceReveal=new T((e,t,r)=>this.diagnose(e,t,r)),this.adContainer=null,this.companionElement=null,this.staticQuartilesFired=new Set,this.sequencer=null,this.pendingAdError=null,this.preRollHoldActive=!1,this.preRollHoldHandler=null,this.breakContentGateHandler=null,this.destroyed=!1,this.abortingBreak=!1,this.overlayImageEl=null,this.imageAssetTimer=null,this.chainResolver=null,this.transitionPhaseReporter=null,this.chaining=!1,this.diagnoseHandler=null,this.playerContainerOriginalStyle="",this.companionRemoveTimer=null,this.adFadeTimer=null,this.volumeChangeCallback=null,this.stage=null,this.resizeObserver=null,this.contentVideoElement=null,this.contentVideoGeometryHandler=null,this.contentVideoMutationObserver=null,this.resumeGeneration=0,this.singleSurfaceDevice=!1,this.currentSelectedFormat=void 0,this.state=m(),this.state.playerContainer=r??null,this.clock=G,this.singleSurfaceDevice=b,this.contentPlayer=e,this.audio=new I({contentPlayer:e,isAdPlaying:()=>this.isPlaying,getVastAdManager:()=>this.vastAdManager,onVolumeChange:(e,t)=>this.volumeChangeCallback?.(e,t),log:(...e)=>this.log(...e)}),this.adBreakCutSafetyMarginSec=l,this.doubleBoxAudio=u,this.container=t,r&&(this.playerContainer=r),this.createAdAdapter=i,this.debug=n,this.state.gamConfig=o,this.preloadMode=d,this.insertionMode=h,this.pauseAdPresenter=new P({contentPlayer:this.contentPlayer,getStage:()=>this.stage,resolveUri:e=>this.resolveUri(e),buildStaticAssetUrl:(e,t)=>this.buildStaticAssetUrl(e,t),diagnose:(e,t,r)=>this.diagnose(e,t,r),log:(...e)=>this.log(...e)}),this.assetParameters=new p(this.state,{contentPlayer:this.contentPlayer,container:this.container,getGamStreamManager:()=>this.state.gamStreamManager,getCurrentBreak:()=>this.currentBreak,getEffectiveBreakDuration:()=>this.effectiveBreakDurationSec,diagnose:(e,t,r)=>this.diagnose(e,t,r),log:(...e)=>this.log(...e)}),this.gamPodBridge=new y(this.state,{contentPlayer:this.contentPlayer,debug:this.debug,getAdPlayer:()=>this.adPlayer,getAdContainer:()=>this.adContainer,isSharedElement:()=>"shared-element"===this.effectiveInsertionMode(),getCurrentBreak:()=>this.currentBreak,getCurrentAsset:()=>this.currentAsset,getEffectiveBreakDuration:()=>this.effectiveBreakDurationSec,getAdPlayerStreamRemainingSec:()=>this.adPlayerStreamRemainingSec(),emitStep:e=>this.emitStep(e),emitAdStart:(e,t,r,s,a)=>this.callbacks?.onAdStart(e,t,r,s,a),emitAdEnd:(e,t,r,s,a)=>this.callbacks?.onAdEnd(e,t,r,s,a),onAdEnded:()=>this.onAdEnded(),pushAssetParameters:(e,t)=>this.pushAssetParameters(e,t),assetParameterResolver:this.assetParameters,diagnose:(e,t,r)=>this.diagnose(e,t,r),log:(...e)=>this.log(...e)}),this.adStage=new A({contentPlayer:this.contentPlayer,container:this.container,getPlayerContainer:()=>this.playerContainer,setPlayerContainer:e=>{this.playerContainer=e},setPlayerContainerOriginalStyle:e=>{this.playerContainerOriginalStyle=e},onPlayerContainerCreated:()=>{this.playerContainerCreatedBySDK=!0},getStage:()=>this.stage,setStage:e=>{this.stage=e},getContentVideo:()=>this.contentVideoElement,setContentVideo:e=>{this.contentVideoElement=e},getAdContainer:()=>this.adContainer,setAdContainer:e=>{this.adContainer=e},getAdMediaContainer:()=>this.adMediaContainer,setAdMediaContainer:e=>{this.adMediaContainer=e},setAdPlayer:e=>{g(this.state),this.adPlayer=e,e&&this.audio.attachAdPlayer(e)},createAdAdapter:e=>this.createAdAdapter(e),setupEventListeners:()=>this.setupEventListeners(),resizeVast:()=>this.vastAdManager?.resize(),getVariant:e=>this.getVariant(e),getCurrentBreak:()=>this.currentBreak,buildStaticAssetUrl:(e,t)=>this.buildStaticAssetUrl(e,t),getCompanion:()=>this.companionElement,setCompanion:e=>{this.companionElement=e},getOverlayImage:()=>this.overlayImageEl,setOverlayImage:e=>{this.overlayImageEl=e},getImageAssetTimer:()=>this.imageAssetTimer,setImageAssetTimer:e=>{this.imageAssetTimer=e},isChaining:()=>this.chaining,log:(...e)=>this.log(...e),presentationHost:C(()=>v(this.playerContainer,this.adContainer,this.stage))}),this.preloader=new k({state:this.state,clock:this.clock,adStartBudgetMs:AdPlayerController.AD_START_BUDGET_MS,gamPodAdStartBudgetMs:AdPlayerController.GAM_POD_AD_START_BUDGET_MS,minAdStartBudgetMs:AdPlayerController.MIN_AD_START_BUDGET_MS,debug:this.debug,isPlaying:()=>this.isPlaying,preloadMode:this.preloadMode,effectiveInsertionMode:()=>this.effectiveInsertionMode(),getVariant:e=>this.getVariant(e),getAssets:e=>this.getAssets(e),buildStaticAssetUrl:(e,t)=>this.buildStaticAssetUrl(e,t),resolveAssetUri:(e,t,r)=>this.resolveAssetUri(e,t,r),getCompanionVariant:e=>this.getVariant(e),getAdPlayer:()=>this.adPlayer,getAdContainer:()=>this.adContainer,buildVastTagUrl:(e,t)=>this.buildVastTagUrl(e,t),resolveChainedSuccessor:e=>this.resolveChainedSuccessor(e),show:()=>this.show(),reapplyAdContainerLayout:e=>this.reapplyAdContainerLayout(e),getCurrentBreak:()=>this.currentBreak,onBreakVisible:e=>this.callbacks?.onBreakVisible?.(e),onAdError:e=>this.onAdError(e),diagnose:(e,t,r)=>this.diagnose(e,t,r),log:(...e)=>this.log(...e)}),this.presentationModes=new f(this.state,{contentPlayer:this.contentPlayer,getAdPlayer:()=>this.adPlayer,insertionMode:this.insertionMode,getCurrentBreak:()=>this.currentBreak,getCurrentFormat:()=>this.currentSelectedFormat,isSharedElementActive:()=>this.state.sharedAdClaimActive,getVariant:e=>this.getVariant(e),isAdPlaying:()=>this.isAdPlaying(),hasCurrentAsset:()=>null!==this.currentAsset,contentSourceIsReloadable:()=>null!==this.sharedElementPlayer.restorableContentSource(),adoptPictureInPictureMidBreak:()=>this.adoptPictureInPictureMidBreak(),restoreLayout:()=>this.restoreLayout(),applyLayout:e=>this.applyLayout(e),show:()=>this.show(),playAdThroughContentElement:()=>this.sharedElementPlayer.playAdThroughContentElement(),getContentPositionAtBreakStart:()=>this.state.contentPositionAtBreakStart,setContentPositionAtBreakStart:e=>this.resumeCoordinator.reanchorMidBreak(e),gateContentForBreak:()=>this.gateContentForBreak(),diagnose:(e,t,r)=>this.diagnose(e,t,r),log:(...e)=>this.log(...e)}),this.resumeCoordinator=new B(this.state,{contentPlayer:this.contentPlayer,clock:this.clock,getInsertionMode:()=>this.effectiveInsertionMode(),gateContentForBreak:()=>this.gateContentForBreak(),clearContentGate:e=>this.clearContentGate(e),muteContent:()=>this.audio.mute("content"),diagnose:(e,t,r)=>this.diagnose(e,t,r),log:(...e)=>this.log(...e)}),this.recoveryDeps={contentPlayer:e,getCallbacks:()=>this.callbacks,preloadMode:d,contentRecovery:w,resumeCoordinator:this.resumeCoordinator,diagnose:(e,t,r)=>this.diagnose(e,t,r),log:(...e)=>this.log(...e)},this.sharedElementPlayer=new S(this.state,{contentPlayer:this.contentPlayer,getAdPlayer:()=>this.adPlayer,surfaceReveal:this.surfaceReveal,resolveAssetUri:(e,t)=>this.resolveAssetUri(e,t),liveEdgeDistanceSec:()=>this.resumeCoordinator.liveEdgeDistanceSec(),rebindContentElementIfSwapped:()=>this.rebindContentElementIfSwapped(),createAdAdapter:(e,t)=>this.createAdAdapter(e,t),reportAdProgress:e=>this.reportAdProgress(e),processTimedMetadata:e=>this.gamPodBridge.processTimedMetadata(e),diagnose:(e,t,r)=>this.diagnose(e,t,r),log:(...e)=>this.log(...e),clearContentGate:()=>this.clearContentGate(),hide:()=>this.hide(),waitForLiveEdge:e=>this.resumeCoordinator.waitForLiveEdge(e),getVariant:e=>this.getVariant(e),getAssets:e=>this.getAssets(e)}),this.variantSelector=new R(this.state,{getEffectiveInsertionMode:()=>this.effectiveInsertionMode(),singleSurfaceDevice:this.singleSurfaceDevice,preloadMode:this.preloadMode,getIsContentPictureInPicture:()=>this.isContentPictureInPicture(),getIsGamReady:()=>this.isGamReady(),getSupportsParallelBuffering:()=>this.contentPlayer.supportsParallelBuffering,diagnose:(e,t,r)=>this.diagnose(e,t,r)}),this.successorQueue=new V({getAdPlayer:()=>this.adPlayer,isEnabled:()=>"parallel"===this.preloadMode&&"overlay"===this.effectiveInsertionMode()&&!this.state.sharedAdClaimActive,buildStaticAssetUrl:(e,t)=>this.buildStaticAssetUrl(e,t),getCallbacks:()=>this.state.callbacks,log:(...e)=>this.log(...e)}),this.assetPlayback=new E(this.state,{getAdPlayer:()=>this.adPlayer,getAdContainer:()=>this.adContainer,getAdMediaContainer:()=>this.adMediaContainer,clearStaticQuartiles:()=>this.staticQuartilesFired.clear(),getVariant:e=>this.getVariant(e),getGamEventCallbacks:()=>this.gamEventCallbacks,getSequencer:()=>this.sequencer,debug:this.debug,buildStaticAssetUrl:(e,t)=>this.buildStaticAssetUrl(e,t),buildGamUrl:(e,t)=>this.buildGamUrl(e,t),buildVastTagUrl:(e,t)=>this.buildVastTagUrl(e,t),onAdError:e=>this.onAdError(e),onAdEnded:()=>this.onAdEnded(),drive:e=>this.drive(e),clearOverlayImage:()=>this.clearOverlayImage(),revealFirstFrame:()=>this.preloader.revealFirstFrame(),surfaceReveal:this.surfaceReveal,transferPictureInPictureToAd:()=>this.presentationModes.transferPictureInPictureToAd(),isGamPodAsset:e=>this.isGamPodAsset(e),hasGamIdentityMismatch:e=>this.gamPodBridge.hasIdentityMismatch(e),beginGamPodTracking:e=>this.gamPodBridge.beginGamPodTracking(e),recordAdProgress:e=>this.adPlayerEvents.recordAdProgress(e),reportPhase:e=>this.reportPhase(e),armAdStartBudget:e=>this.armAdStartBudget(e),takeQueuedSuccessor:(e,t,r)=>this.successorQueue.take(e,t,r),queueUpcomingAd:()=>{const e=this.sequencer?.upcomingAsset();e&&this.currentBreak&&this.successorQueue.prepare(this.currentBreak,e,!1)},remainingAdStartBudgetMs:()=>this.remainingAdStartBudgetMs(),loadWithinBudget:(e,t)=>this.loadWithinBudget(e,t),waitForFirstFrame:()=>this.waitForFirstFrame(),getOverlayImage:()=>this.overlayImageEl,setOverlayImage:e=>{this.overlayImageEl=e},getImageAssetTimer:()=>this.imageAssetTimer,setImageAssetTimer:e=>{this.imageAssetTimer=e},getVastPodPosition:()=>this.vastPodPosition,setVastPodPosition:e=>{this.vastPodPosition=e},diagnose:(e,t,r)=>this.diagnose(e,t,r),log:(...e)=>this.log(...e)}),this.breakSequenceDriver=new M(this.state,{successorQueue:this.successorQueue,getAssets:e=>this.getAssets(e),getChainResolver:()=>this.chainResolver,getEffectiveInsertionMode:()=>this.effectiveInsertionMode(),getVastPodPosition:()=>this.vastPodPosition,setVastPodPosition:e=>{this.vastPodPosition=e},getPendingAdError:()=>this.pendingAdError,setPendingAdError:e=>{this.pendingAdError=e},getChaining:()=>this.chaining,setChaining:e=>{this.chaining=e},isExpandingPodAsset:e=>this.isExpandingPodAsset(e),fallBackToPodLevelReporting:()=>this.fallBackToPodLevelReporting(),resetGamPodTracking:()=>this.resetGamPodTracking(),emitGamPodSteps:e=>this.gamPodBridge.emitGamPodSteps(e),playAsset:(e,t,r)=>this.playAsset(e,t,r),endBreak:()=>this.endBreak(),playBreak:(e,t,r)=>this.playBreak(e,t,r),diagnose:(e,t,r)=>this.diagnose(e,t,r),log:(...e)=>this.log(...e)}),this.adPlayerEvents=new F(this.state,{getAdPlayer:()=>this.adPlayer,getContentPlayer:()=>this.contentPlayer,getGamEventCallbacks:()=>this.gamEventCallbacks,getSequencer:()=>this.sequencer,onAdEnded:()=>this.onAdEnded(),processTimedMetadata:e=>this.gamPodBridge.processTimedMetadata(e),toAdError:e=>this.toAdError(e),revealFirstFrame:()=>this.revealFirstFrame(),clearFirstFrameResolver:()=>this.preloader.clearFirstFrameResolver(),drive:e=>this.drive(e),setPendingAdError:e=>{this.pendingAdError=e},dropQueuedAd:()=>this.successorQueue.drop(),log:(...e)=>this.log(...e),maybePrepositionContentForResume:()=>"single-decoder"!==this.preloadMode&&this.resumeCoordinator.maybePrepositionContentForResume(),maybeCatchUpContentForResume:e=>"single-decoder"!==this.preloadMode&&this.resumeCoordinator.maybeCatchUpContentForResume(e),getStaticQuartilesFired:()=>this.staticQuartilesFired,onGamPodAdProgress:e=>this.gamPodBridge.onGamPodAdProgress(e)})}setGamEventCallbacks(e){this.gamPodBridge.setEventCallbacks(e)}setDiagnoseHandler(e){this.diagnoseHandler=e}diagnose(e,t,r){this.diagnoseHandler?.(e,t,r)}holdContentForPreRoll(e){if(this.preRollHoldActive)return;const t=this.getVariant(e);t&&this.shouldPauseContent(t.format)&&(this.preRollHoldActive=!0,this.contentPlayer.pause(),this.preRollHoldHandler=()=>{this.preRollHoldActive&&this.contentPlayer.pause()},this.contentPlayer.on("playing",this.preRollHoldHandler),this.diagnose("DA-PREROLL-CONTENT-HELD","Content held at session start until the pending pre-roll begins.",{breakId:e.id}))}releaseContentHold(){this.preRollHoldActive&&(this.preRollHoldActive=!1,this.preRollHoldHandler&&(this.contentPlayer.off("playing",this.preRollHoldHandler),this.preRollHoldHandler=null))}gateContentForBreak(){this.contentPlayer.pause(),this.breakContentGateHandler||(this.breakContentGateHandler=()=>{this.contentPaused&&!this.state.resumeCatchupActive&&this.contentPlayer.pause()},this.contentPlayer.on("playing",this.breakContentGateHandler))}clearContentGate(e=!1){this.resumeCoordinator.endResumeCatchup(e),this.breakContentGateHandler&&(this.contentPlayer.off("playing",this.breakContentGateHandler),this.breakContentGateHandler=null)}setChainResolver(e){this.chainResolver=e}onChainSuccessorChanged(){this.sequencer&&this.currentBreak&&this.isPlaying&&this.drive(this.sequencer.onOutcome({kind:"successor-changed"}),!0)}setResumeCueAnchorResolver(e){this.resumeCoordinator.setCueAnchorResolver(e)}setResumeCueEndResolver(e){this.resumeCoordinator.setCueEndResolver(e)}ingestStreamClockCue(e){this.resumeCoordinator.ingestStreamClockCue(e)}setTransitionPhaseReporter(e){this.transitionPhaseReporter=e}reportPhase(e){this.transitionPhaseReporter?.(e)}setBreakResumePolicy(e,t,r=!1){this.adInsertionType=e,this.breakTimebase=t,this.state.continueContentDuringBreak=r,this.state.continueRefusalReported=!1}initialize(){this.adContainer||(this.setupOverlay(),this.presentationModes.setupPictureInPictureTracking(),"adaptive"===this.insertionMode&&this.presentationModes.setupFullscreenTracking())}async startGamSession(e,t){await this.gamPodBridge.startGamSession(e,t)}endGamSession(){this.gamPodBridge.endGamSession()}setSessionAssetParameters(e){this.assetParameters.setSessionAssetParameters(e)}setManifestAssetParameters(e){this.assetParameters.setManifestAssetParameters(e)}updateAssetParameters(e){this.assetParameters.updateAssetParameters(e)}resolveExternalVendorConfiguration(e){return this.assetParameters.resolveExternalVendorConfiguration(e)}updateAssetParameterMacros(e){this.assetParameters.updateAssetParameterMacros(e)}get updatedAssetParameters(){return this.state.updatedAssetParameters}setSessionAssetParameterMacros(e){this.assetParameters.setSessionAssetParameterMacros(e)}get sessionAssetParameterMacros(){return this.assetParameters.getSessionAssetParameterMacros()}buildVastTagUrl(e,t){return this.assetParameters.buildVastTagUrl(e,t)}buildStaticAssetUrl(e,t){return this.assetParameters.buildStaticAssetUrl(e,t)}pushAssetParameters(e,t){this.assetParameters.pushAssetParameters(e,t)}setupOverlay(){this.adStage.setupOverlay()}isGamPodAsset(e){return this.gamPodBridge.isGamPodAsset(e)}isExpandingPodAsset(e){return this.gamPodBridge.isExpandingPodAsset(e)}fallBackToPodLevelReporting(){this.gamPodBridge.fallBackToPodLevelReporting()}resetGamPodTracking(){this.gamPodBridge.resetGamPodTracking()}emitGamPodSteps(e){this.gamPodBridge.emitGamPodSteps(e)}isGamEnabled(){return this.gamPodBridge.isGamEnabled()}isGamReady(){return this.gamPodBridge.isGamReady()}isOverlayInsertion(){return"overlay"===this.effectiveInsertionMode()}settleAssetProgress(){this.adPlayerEvents.settleAssetProgress()}reportAdProgress(e){this.adPlayerEvents.reportAdProgress(e)}setupEventListeners(){this.adPlayerEvents.setupEventListeners()}async preload(e){return this.preloader.preload(e)}renderLshapeContentBackdrop(e){this.adStage.renderLshapeContentBackdrop(e)}currentInsertionMode(){return this.presentationModes.currentInsertionMode()}effectiveInsertionMode(){return this.presentationModes.effectiveInsertionMode()}isContentPictureInPicture(){return this.presentationModes.isPictureInPicture()}rebindContentElementIfSwapped(){this.presentationModes.rebindContentElementIfSwapped()}teardownFullscreenTracking(){this.presentationModes.teardownFullscreenTracking()}teardownPictureInPictureTracking(){this.presentationModes.teardownPictureInPictureTracking()}isPictureInPicture(){return this.presentationModes.isPictureInPicture()}pictureInPictureOwner(){return this.presentationModes.pictureInPictureOwner()}isShowingAd(){if(!this.isAdPlaying())return!1;const e=this.currentDeclaredFormat();return!n(e,this.isContentPictureInPicture())}currentDeclaredFormat(){return this.currentSelectedFormat??(this.currentBreak?h(this.currentBreak):void 0)}async playBreak(t,s,a){if(this.state.preloadGeneration++,this.resumeGeneration++,!this.sharedElementPlayer.beginSharedActivity())return;this.releaseContentHold(),this.rebindContentElementIfSwapped();const n=a?.effectiveDurationSec??null;this.effectiveBreakDurationSec=null!==n&&n>0?n:null;const d=this.effectiveBreakDurationSec??t.duration;if("shared-element"===this.effectiveInsertionMode())return g(this.state),void await this.sharedElementPlayer.playSharedElementBreak(t,s);if(!this.adPlayer)return void this.log("playBreak called before initialize()");const h=this.getVariant(t);if(!h)return void this.log("No valid variant found for break:",t.id);this.currentBreak=t,this.callbacks=s,this.isPlaying=!0,this.firstFrameGateActive=a?.firstFrameGate??!1,this.firstFrameGateShown=!1;const l=this.continueContentDuringBreakReason(h.format),u=r(t),c=this.shouldPauseContent(h.format)&&"ok"===l&&!u;c||u||this.state.continueRefusalReported||!this.shouldPauseContent(h.format)||(this.state.continueRefusalReported=!0,this.diagnose("DA-CONTINUE-PLAYOUT-REFUSED",`Content will be paused for breaks this session: ${l}`,{reason:l,format:h.format})),h.format===e.DOUBLE?this.audio.mute("ad"===this.doubleBoxAudio?"content":"ad"):c?this.audio.mute("content"):this.shouldPauseContent(h.format)&&this.audio.unmute("ad");const m=this.declaredFormatOf(t);if(this.currentSelectedFormat=m,this.state.pipAdoptedForBreak=i(m,this.isContentPictureInPicture()),this.state.pipAdoptedForBreak&&this.diagnose("DA-PIP-FORMAT-OVERRIDE",`Picture-in-picture: ${m} break plays as single`,{declared:m,effective:e.SINGLE,reason:"picture-in-picture",midBreak:!1}),this.shouldPauseContent(h.format)?(this.resumeCoordinator.captureBreakAnchor(t),c?(this.contentPaused=!1,this.resumeCoordinator.beginContinueContentPlayout()):(this.contentPaused=!0,this.gateContentForBreak())):this.contentPaused=!1,this.applyLayout(h),h.format===e.LSHAPE_CONTENT)return this.renderLshapeContentBackdrop(h),void(d&&d>0?(this.log("Starting duration timer:",d,"seconds"),this.durationTimer=setTimeout(()=>{this.log("Duration timer expired, ending break"),this.endBreak()},1e3*d)):this.endBreak());this.firstFrameGateActive||(this.show(),this.reapplyAdContainerLayout(h));if(0===this.getAssets(t).length)return this.log("No assets in break, ending immediately"),void this.endBreak();{const e=Math.max(0,d||0)+this.adBreakCutSafetyMarginSec;G(this.state,e,()=>{this.log("Break-cut timer expired, ending break"),this.firstFrameRejector?this.onAdError(new D("Break cut before first frame rendered","DA-AD-START-TIMEOUT")):this.sequencer&&this.currentBreak&&this.isPlaying?(this.callbacks?.onBreakCut?.(this.currentBreak),this.drive(this.sequencer.onOutcome({kind:"break-elapsed"}))):this.endBreak()},()=>this.onAdError(new D("Ad playback startup timed out","DA-AD-START-TIMEOUT")))}this.sequencer=new o("overlay",()=>null,this.selectionInputs(),e=>this.resolveChainedSuccessor(e)),await this.drive(this.sequencer.beginBreak(t)),this.preloadChainedSuccessor(t.id)}get resumeHandoverInMotion(){return this.state.resumeCatchupActive}reapplyAdContainerLayout(e){this.adStage.reapplyAdContainerLayout(e)}adoptPictureInPictureMidBreak(){this.presentationModes.adoptPictureInPictureMidBreak()}async restorePictureInPictureToContent(){await this.presentationModes.restorePictureInPictureToContent()}continueContentDuringBreakReason(e){return this.variantSelector.continueContentDuringBreakReason(e)}shouldPauseContent(t){return t===e.SINGLE||t===e.LSHAPE_AD}selectionInputs(){return this.variantSelector.selectionInputs()}breakNothingToPlayReason(e){return this.variantSelector.breakNothingToPlayReason(e)}unknownVariantFormatsOf(e){return this.variantSelector.unknownVariantFormatsOf(e)}diagnoseUnknownVariantFormats(e){this.variantSelector.diagnoseUnknownVariantFormats(e)}declaredFormatOf(e){const{selected:t,deviceQualified:r}=l(e,this.selectionInputs());return(t??r)?.format??h(e)}getVariant(e){return this.variantSelector.getVariant(e)}applyLayout(e){this.adStage.applyLayout(e)}restoreLayout(){this.adStage.restoreLayout()}getAssets(e){return d(this.getVariant(e))}async playAsset(e,t,r){return this.assetPlayback.playAsset(e,t,r)}clearOverlayImage(){this.adStage.clearOverlayImage()}async showPauseImage(e){return this.pauseAdPresenter.showPauseImage(e)}hidePauseImage(){this.pauseAdPresenter.hidePauseImage()}async drive(e,t=!1){return this.breakSequenceDriver.drive(e,t)}emitStep(e){this.breakSequenceDriver.emitStep(e)}async resolveAssetUri(e,t,r){return this.assetPlayback.resolveAssetUri(e,t,r)}async buildGamUrl(e,t){return this.gamPodBridge.buildGamUrl(e,t)}resolveUri(e){return this.assetParameters.resolveUri(e)}adPlayerStreamRemainingSec(){return this.assetPlayback.adPlayerStreamRemainingSec()}onAdEnded(){this.currentBreak&&this.isPlaying&&this.sequencer&&(this.settleAssetProgress(),this.drive(this.sequencer.onOutcome({kind:"asset-ended"})))}toAdError(e){return this.assetPlayback.toAdError(e)}onAdError(e){this.adPlayerEvents.onAdError(e)}endBreak(){if(!this.currentBreak)return;this.log("Break ended:",this.currentBreak.id),w(this.state),this.state.gamPodTracker&&this.emitGamPodSteps(this.state.gamPodTracker.finish()),this.resetGamPodTracking(),this.pushAssetParameters(void 0);const e=this.currentBreak;let s=this.abortingBreak?null:this.resolveChainedSuccessor(e.id);if(s){const e=this.breakNothingToPlayReason(s);this.diagnoseUnknownVariantFormats(s),e&&(this.diagnose("DA-BREAK-NO-PLAYABLE-ASSET",`chained break ${s.id} has no playable variant (${e}); it will not begin.`,{breakId:s.id,reason:e}),this.callbacks?.onAdError(s,void 0,new D(`Break ${s.id} has no playable variant (${e})`,"DA-BREAK-NO-PLAYABLE-ASSET")),this.callbacks?.onChainedBreakSkipped?.(s),s=null)}if(s)return void this.advanceToChainedBreak(e,s);if(this.currentSelectedFormat=void 0,this.resumeCoordinator.endContinueContentPlayout(),this.restorePictureInPictureToContent(),this.isPlaying=!1,this.currentBreak=null,this.resumeGeneration++,this.effectiveBreakDurationSec=null,g(this.state),this.preloader.clearWarmedBreaks(),this.successorQueue.reset(),this.firstFrameGateActive=!1,this.firstFrameGateShown=!1,this.clearFirstFrame("Break ended before first frame"),this.audio.unmuteAll(),this.clearOverlayImage(),this.vastAdManager?.reset(),this.hide(),this.restoreLayout(),this.adPlayer?.pause(),this.state.sharedAdClaimActive){const t=this.callbacks;return this.sharedElementPlayer.detachSharedAdListeners(),this.abortingBreak=!1,this.state.earlyReturnResumeOffsetSec=null,this.sharedElementPlayer.restoreSharedElementContent(e),void t?.onBreakEnd(e)}const a="recreate"===this.recoveryDeps.contentRecovery&&"function"==typeof this.contentPlayer.recreateMediaPipeline,i="single-decoder"===this.preloadMode||a?this.adPlayer?.unload?.():void 0;if(this.contentPaused&&r(e))this.contentPaused=!1,this.clearContentGate();else if(this.contentPaused){this.contentPaused=!1,this.clearContentGate(this.resumeHandoverInMotion);const r=this.resumeGeneration;b(this.recoveryDeps,{breakInfo:e,earlyReturnOffsetSec:this.state.earlyReturnResumeOffsetSec,isLiveResume:this.breakTimebase===t.WALLCLOCK,released:i,isCurrent:()=>r===this.resumeGeneration})}this.abortingBreak=!1,this.state.earlyReturnResumeOffsetSec=null,this.callbacks?.onBreakEnd(e)}currentAdAsset(){return this.isPlaying?this.currentAsset:null}skipBreak(){this.isPlaying&&(this.log("Skipping break:",this.currentBreak?.id),w(this.state),this.sequencer&&this.currentBreak?this.drive(this.sequencer.onOutcome({kind:"break-elapsed"})):this.endBreak())}abortBreak(e){this.isPlaying&&(this.log("Early return — break removed from manifest:",this.currentBreak?.id),this.state.earlyReturnResumeOffsetSec=Math.max(0,e),this.abortingBreak=!0,w(this.state),this.sequencer&&this.currentBreak?this.drive(this.sequencer.onOutcome({kind:"break-elapsed"})):this.endBreak())}stopActiveBreak(){g(this.state),this.successorQueue.reset(),this.sharedElementPlayer.cancelSharedRestore()&&this.isPlaying&&(this.log("Aborting active break on session end:",this.currentBreak?.id),w(this.state),this.sharedElementPlayer.detachSharedAdListeners(),this.sharedElementPlayer.releaseContentElementAdPlayer(),this.adPlayer?.pause(),this.clearOverlayImage(),this.vastAdManager?.reset(),this.hide(),this.restoreLayout(),this.isPlaying=!1,this.currentBreak=null,this.currentAsset=null,this.contentPaused=!1,this.clearContentGate(),this.resumeCoordinator.endContinueContentPlayout(),this.effectiveBreakDurationSec=null,this.firstFrameGateActive=!1,this.firstFrameGateShown=!1,this.clearFirstFrame("Break aborted before first frame"),this.audio.unmuteAll())}resolveChainedSuccessor(e){return this.breakSequenceDriver.resolveChainedSuccessor(e)}advanceToChainedBreak(e,t){this.breakSequenceDriver.advanceToChainedBreak(e,t)}preloadChainedSuccessor(e){this.preloader.preloadChainedSuccessor(e)}revealFirstFrame(){this.assetPlayback.revealFirstFrame()}armAdStartBudget(e){this.preloader.armAdStartBudget(e)}remainingAdStartBudgetMs(){return this.preloader.remainingAdStartBudgetMs()}loadWithinBudget(e,t){return this.preloader.loadWithinBudget(e,t)}async waitForFirstFrame(){return this.preloader.waitForFirstFrame()}clearFirstFrame(e="Break ended before first frame"){this.preloader.clearFirstFrame(e)}show(){this.adStage.show(),this.surfaceReveal.surfaceShown()}hide(){this.adStage.hide(),this.surfaceReveal.surfaceHidden()}isAdPlaying(){return this.isPlaying}get muted(){return this.audio.muted}set muted(e){this.audio.muted=e}get volume(){return this.audio.volume}set volume(e){this.audio.volume=e}setVolumeChangeCallback(e){this.volumeChangeCallback=e}destroy(){if(this.destroyed)return;if(this.resumeGeneration++,g(this.state),this.successorQueue.reset(),!this.sharedElementPlayer.beginSharedActivity())return;this.destroyed=!0,this.adStage.destroy(),this.resumeCoordinator.cancelResumeLandedProbe(),this.resumeCoordinator.dispose(),this.resumeCoordinator.endResumeCatchup(),this.resumeCoordinator.endContinueContentPlayout(),this.state.resumePrepositionSettle&&(clearTimeout(this.state.resumePrepositionSettle),this.state.resumePrepositionSettle=null),this.releaseContentHold(),this.clearContentGate(),this.clearFirstFrame("Controller destroyed before first frame"),this.firstFrameGateActive=!1,this.firstFrameGateShown=!1,this.resetGamPodTracking(),w(this.state),this.companionRemoveTimer&&(clearTimeout(this.companionRemoveTimer),this.companionRemoveTimer=null),this.adFadeTimer&&(clearTimeout(this.adFadeTimer),this.adFadeTimer=null),this.adPlayer?.pause(),this.clearOverlayImage(),this.pauseAdPresenter.destroy(),this.audio.destroy(),this.sharedElementPlayer.detachSharedAdListeners(),this.sharedElementPlayer.releaseContentElementAdPlayer(),this.state.pipTransferredToAd=!1,this.teardownFullscreenTracking(),this.teardownPictureInPictureTracking(),this.hide(),this.adStage.destroy(),this.resizeObserver?.disconnect(),this.resizeObserver=null,this.contentVideoMutationObserver?.disconnect(),this.contentVideoMutationObserver=null,this.contentVideoElement&&this.contentVideoGeometryHandler&&(this.contentVideoElement.removeEventListener("loadedmetadata",this.contentVideoGeometryHandler),this.contentVideoElement.removeEventListener("resize",this.contentVideoGeometryHandler)),this.contentVideoElement=null,this.contentVideoGeometryHandler=null;const e=this.adStage.restoreContent(this.playerContainerCreatedBySDK,this.playerContainerOriginalStyle);this.companionElement&&this.companionElement.parentElement&&(this.companionElement.parentElement.removeChild(this.companionElement),this.companionElement=null),this.isPlaying=!1,this.currentBreak=null,this.adContainer&&this.adContainer.parentElement&&this.adContainer.parentElement.removeChild(this.adContainer),!e&&this.stage&&this.stage.parentElement&&this.stage.parentElement.removeChild(this.stage),this.stage=null,this.adContainer=null,this.adMediaContainer=null;try{this.adPlayer?.destroy()}catch(e){this.log("Ad player adapter destroy failed (ignored):",e)}this.adPlayer=null,this.callbacks=null,this.gamPodBridge.destroy(),this.vastAdManager?.destroy(),this.vastAdManager=null,this.log("Ad player destroyed")}log(...e){this.debug&&u("[OptiViewAds:AdPlayerController]",...e)}}AdPlayerController.AD_START_BUDGET_MS=5e3,AdPlayerController.GAM_POD_AD_START_BUDGET_MS=1e4,AdPlayerController.MIN_AD_START_BUDGET_MS=3e3;
@@ -0,0 +1,43 @@
1
+ import type { Break } from '../types/BreakManifest';
2
+ import type { TrackedBreak } from './RewindRearm';
3
+ /**
4
+ * The active break's chained successor appeared, changed or (`null`) disappeared after a
5
+ * manifest update. The renderer re-prepares or drops what it buffered for the switch.
6
+ */
7
+ export type ChainSuccessorChangedCallback = (activeBreak: Break, successor: Break | null) => void;
8
+ export interface BreakChainDeps {
9
+ breaks: Map<string, TrackedBreak>;
10
+ enabled: boolean;
11
+ maxGapMs: number;
12
+ toleranceMs: number;
13
+ getBreakStartTime: (breakInfo: Break) => number | null;
14
+ log: (...args: unknown[]) => void;
15
+ }
16
+ /** What a renderer buffers for a successor; two breaks with the same signature play the same media. */
17
+ export declare function chainSuccessorSignature(breakInfo: Break): string;
18
+ /**
19
+ * Back-to-back breaks played as one chain: the successors within the chaining gap are CHAINED so
20
+ * the time-based check never starts them on its own, and a manifest update while a break is
21
+ * active re-walks the chain, so a break announced during the chain joins it at once.
22
+ */
23
+ export declare class BreakChain {
24
+ private readonly deps;
25
+ private onSuccessorChanged;
26
+ /** The successor last seen for the active break; a later manifest update is compared to it. */
27
+ private successorSeen;
28
+ constructor(deps: BreakChainDeps);
29
+ setOnSuccessorChanged(callback: ChainSuccessorChangedCallback | null): void;
30
+ /** Mark every PENDING break that chains after `breakId` as CHAINED. */
31
+ claim(breakId: string): void;
32
+ /** The PENDING/CHAINED break starting within the chaining gap after this break's end, or null. */
33
+ successorOf(breakId: string): Break | null;
34
+ /** Promote a CHAINED (or PENDING) break to ACTIVE as the chain advances into it. */
35
+ activate(breakId: string): void;
36
+ /** After a manifest update: re-walk the chain from the active break and report a changed successor. */
37
+ refresh(active: Break): void;
38
+ private rememberSuccessor;
39
+ /** Claim the breaks now in the chain; release a CHAINED break that moved out of it. */
40
+ private reclaim;
41
+ /** Earliest break starting within the chaining gap after `current`, restricted to `states`. */
42
+ private findSuccessor;
43
+ }
@@ -0,0 +1 @@
1
+ import{BreakState as e}from"./BreakSchedulerTypes";import{variantsOf as s}from"./selectVariant";function t(e){const s=e.uri;return void 0===s?null:"string"==typeof s?s:s.map(e=>e.value)}export function chainSuccessorSignature(e){return JSON.stringify([e.id,e.start,e.duration,s(e).map(e=>[e.format,e.assets.map(e=>[e.id,e.type,t(e),e.duration??null])])])}export class BreakChain{constructor(e){this.deps=e,this.onSuccessorChanged=null,this.successorSeen=null}setOnSuccessorChanged(e){this.onSuccessorChanged=e}claim(s){if(!this.deps.enabled)return;let t=this.deps.breaks.get(s)?.break??null;for(;t;){const s=this.findSuccessor(t,[e.PENDING]);if(!s)break;const i=this.deps.breaks.get(s.id);if(!i||i.state!==e.PENDING)break;i.state=e.CHAINED,this.deps.log(`🔗 Break "${s.id}" claimed as chained after "${t.id}"`),t=s}this.rememberSuccessor(s)}successorOf(s){if(!this.deps.enabled)return null;const t=this.deps.breaks.get(s)?.break;return t?this.findSuccessor(t,[e.PENDING,e.CHAINED]):null}activate(s){const t=this.deps.breaks.get(s);t&&(t.state=e.ACTIVE,this.rememberSuccessor(s))}refresh(e){if(!this.deps.enabled)return;null!==this.deps.getBreakStartTime(e)&&this.reclaim(e);const s=this.successorOf(e.id),t=s?chainSuccessorSignature(s):null,i=this.successorSeen;this.successorSeen={activeId:e.id,signature:t},i?.activeId===e.id&&i.signature!==t&&(this.deps.log(`🔗 Successor of "${e.id}" is now ${s?`"${s.id}"`:"none"}`),this.onSuccessorChanged?.(e,s))}rememberSuccessor(e){const s=this.successorOf(e);this.successorSeen={activeId:e,signature:s?chainSuccessorSignature(s):null}}reclaim(s){const t=new Set;let i=s;for(;i;){const s=this.findSuccessor(i,[e.PENDING,e.CHAINED]);if(!s||t.has(s.id))break;t.add(s.id),i=s}for(const[i,r]of this.deps.breaks)t.has(i)&&r.state===e.PENDING?(r.state=e.CHAINED,this.deps.log(`🔗 Break "${i}" claimed as chained during "${s.id}"`)):t.has(i)||r.state!==e.CHAINED||(r.state=e.PENDING,this.deps.log(`Break "${i}" left the chain of "${s.id}"`))}findSuccessor(e,s){const t=this.deps.getBreakStartTime(e);if(null===t)return null;const i=t+1e3*(e.duration??0);let r=null;for(const n of this.deps.breaks.values()){if(n.break.id===e.id)continue;if(!s.includes(n.state))continue;const a=this.deps.getBreakStartTime(n.break);if(null===a)continue;if(a<=t)continue;const c=a-i;c>this.deps.maxGapMs||(c<-this.deps.toleranceMs||(!r||a<r.start)&&(r={break:n.break,start:a}))}return r?.break??null}}
@@ -0,0 +1,63 @@
1
+ import { type Break } from '../types/BreakManifest';
2
+ import { type Clock } from './BreakSchedulerTypes';
3
+ import type { BreakSwitchPath, BreakTransitionMeasurement } from './BreakTransitionTimer';
4
+ /** One break of a chain in a `DA-BREAK-CHAIN` report. */
5
+ export interface ChainBreakEntry {
6
+ id: string;
7
+ /** The manifest `start` (wallclock ISO string or timebase seconds); absent for an event-triggered break. */
8
+ scheduledStart?: string | number;
9
+ plannedDurationSec: number;
10
+ beganAtMs: number;
11
+ endedAtMs?: number;
12
+ /** Seconds from its begin to its end, one decimal. */
13
+ playedSec?: number;
14
+ /** It joined the chain after the chain began (published, or moved into the gap, by a poll). */
15
+ announcedDuringChain: boolean;
16
+ /** How the switch into it went; absent for the chain's first break and when the switch was not measured. */
17
+ path?: BreakSwitchPath;
18
+ }
19
+ /** One measured switch between two breaks of a chain. */
20
+ export interface ChainSwitchEntry {
21
+ toBreakId: string;
22
+ gapMs: number;
23
+ path: BreakSwitchPath;
24
+ leadMs?: number;
25
+ }
26
+ /** The `DA-BREAK-CHAIN` summary of one chain, from its first break's begin to content again. */
27
+ export interface BreakChainReport {
28
+ /** The chain's first break. */
29
+ chainId: string;
30
+ breaks: ChainBreakEntry[];
31
+ switches: ChainSwitchEntry[];
32
+ /** Milliseconds from the last break's end to content playing; absent when content did not resume. */
33
+ contentResumeMs?: number;
34
+ /** Milliseconds from the first break's begin to the last break's end. */
35
+ totalMs: number;
36
+ }
37
+ /**
38
+ * Collects one chain of breaks and reports it once, when content plays again or the chain is
39
+ * flushed (a new chain begins, the session ends). Fed by the SDK's break lifecycle and by the
40
+ * {@link BreakTransitionTimer} measurements; side-effect free with an injected clock.
41
+ */
42
+ export declare class BreakChainRecorder {
43
+ private readonly onReport;
44
+ private readonly clock;
45
+ private breaks;
46
+ private switches;
47
+ private knownSuccessorId;
48
+ private announced;
49
+ /** The last break ended; the chain continues only if a chained break begins next. */
50
+ private lastEnded;
51
+ constructor(onReport: (report: BreakChainReport) => void, clock?: Clock);
52
+ /** A break began. A non-chained begin reports any open chain and starts a new one. */
53
+ breakBegan(breakInfo: Break, chained: boolean, knownSuccessorId: string | null): void;
54
+ /** The scheduler reported a new successor of the active break; a new id joined during the chain. */
55
+ successorChanged(successorId: string | null): void;
56
+ breakEnded(breakId: string): void;
57
+ /** A transition measurement: a switch into a chained break, or content playing after the chain. */
58
+ transitionMeasured(measurement: BreakTransitionMeasurement): void;
59
+ /** Report the open chain, if any, without a content resume time. */
60
+ flush(): void;
61
+ private entryFor;
62
+ private emit;
63
+ }
@@ -0,0 +1 @@
1
+ import{isEventTrigger as s}from"../types/BreakManifest";import{systemClock as t}from"./BreakSchedulerTypes";export class BreakChainRecorder{constructor(s,e=t){this.onReport=s,this.clock=e,this.breaks=[],this.switches=[],this.knownSuccessorId=null,this.announced=new Set,this.lastEnded=!1}breakBegan(t,e,n){e||this.flush();const i=t.start;this.breaks.push({id:t.id,...s(i)?{}:{scheduledStart:i},plannedDurationSec:t.duration,beganAtMs:this.clock.now(),announcedDuringChain:e&&this.announced.has(t.id)}),this.knownSuccessorId=n,this.lastEnded=!1}successorChanged(s){this.breaks.length>0&&null!==s&&s!==this.knownSuccessorId&&this.announced.add(s),this.knownSuccessorId=s}breakEnded(s){const t=this.entryFor(s);if(!t||void 0!==t.endedAtMs)return;const e=this.clock.now();t.endedAtMs=e,t.playedSec=Math.round(Math.max(0,e-t.beganAtMs)/100)/10,this.lastEnded=!0}transitionMeasured(s){if("break-to-break"===s.kind){const t=this.entryFor(s.breakId);if(!t)return;const e=s.path??"cold";t.path=e,this.switches.push({toBreakId:s.breakId,gapMs:s.durationMs,path:e,...void 0!==s.leadMs?{leadMs:s.leadMs}:{}})}else"out-of-break"===s.kind&&this.lastEnded&&this.emit(s.durationMs)}flush(){this.breaks.length>0&&this.emit(void 0)}entryFor(s){for(let t=this.breaks.length-1;t>=0;t--)if(this.breaks[t].id===s)return this.breaks[t]}emit(s){const t=this.breaks[0],e=this.breaks[this.breaks.length-1].endedAtMs??this.clock.now(),n={chainId:t.id,breaks:this.breaks,switches:this.switches,...void 0!==s?{contentResumeMs:s}:{},totalMs:Math.max(0,e-t.beganAtMs)};this.breaks=[],this.switches=[],this.knownSuccessorId=null,this.announced=new Set,this.lastEnded=!1,this.onReport(n)}}
@@ -0,0 +1,13 @@
1
+ export declare class BreakPreloadLedger {
2
+ private readonly preloadedBreakIds;
3
+ private readonly rejectedBreakIds;
4
+ private retryRejectedPreloadsAtStart;
5
+ wasPreloaded(breakId: string): boolean;
6
+ wasRejected(breakId: string): boolean;
7
+ shouldRetryRejectedAtStart(breakId: string): boolean;
8
+ checkAtBreakStart(wasHeld: boolean, check: () => void): void;
9
+ recordAttempt(breakId: string, accepted: boolean): void;
10
+ deletePreloaded(breakId: string): void;
11
+ delete(breakId: string): void;
12
+ clear(): void;
13
+ }
@@ -0,0 +1 @@
1
+ export class BreakPreloadLedger{constructor(){this.preloadedBreakIds=new Set,this.rejectedBreakIds=new Set,this.retryRejectedPreloadsAtStart=!1}wasPreloaded(e){return this.preloadedBreakIds.has(e)}wasRejected(e){return this.rejectedBreakIds.has(e)}shouldRetryRejectedAtStart(e){return this.retryRejectedPreloadsAtStart&&this.wasRejected(e)}checkAtBreakStart(e,t){this.retryRejectedPreloadsAtStart=e;try{t()}finally{this.retryRejectedPreloadsAtStart=!1}}recordAttempt(e,t){t?(this.rejectedBreakIds.delete(e),this.preloadedBreakIds.add(e)):this.rejectedBreakIds.add(e)}deletePreloaded(e){this.preloadedBreakIds.delete(e)}delete(e){this.deletePreloaded(e),this.rejectedBreakIds.delete(e)}clear(){this.preloadedBreakIds.clear(),this.rejectedBreakIds.clear()}}
@@ -3,8 +3,10 @@ import type { AdBreakSkipReason } from '../types/Events';
3
3
  import { Break, BreakManifest } from '../types/BreakManifest';
4
4
  import { type ResolvedBreakWarnings } from '../types/OptiViewAdsConfig';
5
5
  import type { MetadataCueTracker } from './MetadataCueTracker';
6
+ import { type ChainSuccessorChangedCallback } from './BreakChain';
6
7
  import { type BreakAbortedCallback, type BreakApproachingCallback, type BreakStartCallback, type BreakWarningCallback, type Clock, type SchedulerDiagnose, type TuneInOptions } from './BreakSchedulerTypes';
7
8
  export { BreakState, systemClock, type BreakAbortedCallback, type BreakApproachingCallback, type BreakStartCallback, type BreakStartContext, type BreakWarningCallback, type Clock, type SchedulerDiagnose, type TuneInOptions, } from './BreakSchedulerTypes';
9
+ export type { ChainSuccessorChangedCallback } from './BreakChain';
8
10
  /** Scheduling cadence; a break is discovered up to one interval late on the tick alone. */
9
11
  export declare const TICK_INTERVAL_MS = 250;
10
12
  export declare class BreakScheduler {
@@ -27,12 +29,13 @@ export declare class BreakScheduler {
27
29
  private onBreakWarning;
28
30
  private onBreakAborted;
29
31
  private onBreakSkipped;
32
+ private beforeBreakCheck;
30
33
  private breakStartsHeld;
34
+ private readonly preloadLedger;
31
35
  private onAdStartDelayElapsed;
32
36
  private adStartDelayElapsedNotified;
33
37
  /** Break ids whose early-return abort was already notified; polls keep arriving during the teardown. */
34
38
  private abortNotifiedBreakIds;
35
- private preloadedBreakIds;
36
39
  private breakWarnings;
37
40
  /** Warning thresholds already fired per break, so each `breakWarnings.seconds` value emits once. */
38
41
  private breakWarningFired;
@@ -46,8 +49,7 @@ export declare class BreakScheduler {
46
49
  /** Wallclock instant content playback ended (spec `event: 'end'`), or null while content plays. */
47
50
  private playbackEndedAtMs;
48
51
  private debug;
49
- private chainingEnabled;
50
- private maxChainGapMs;
52
+ private readonly chain;
51
53
  private tuneInEnabled;
52
54
  private minBreakDurationMs;
53
55
  private clock;
@@ -97,9 +99,11 @@ export declare class BreakScheduler {
97
99
  getChainedSuccessor(breakId: string): Break | null;
98
100
  /** Promote a CHAINED (or PENDING) break to ACTIVE as the chain advances, so completeBreak() cleans it up. */
99
101
  activateChainedBreak(breakId: string): void;
100
- /** Earliest break starting within the chaining gap after `current`, restricted to `states`. */
101
- private findSuccessor;
102
+ /** Notified when a manifest update adds, changes or removes the active break's chained successor. */
103
+ setOnChainSuccessorChanged(callback: ChainSuccessorChangedCallback | null): void;
102
104
  updateManifest(manifest: BreakManifest): void;
105
+ applyEventBoundary(manifest: BreakManifest): void;
106
+ setEventBoundaryCheck(check: ((positionMs: number | null) => boolean) | null): void;
103
107
  /**
104
108
  * Re-align the schedule to a manifest from a different channel (see `detectChannelSwitch`).
105
109
  * Every ledger of the previous channel is dropped so a break id reused by the new channel
@@ -139,6 +143,11 @@ export declare class BreakScheduler {
139
143
  */
140
144
  private armStartDeadline;
141
145
  private clearStartDeadline;
146
+ /** Halt the tick, keeping tracked breaks and callbacks; {@link resume} restarts it. */
147
+ suspend(): void;
148
+ resume(): void;
149
+ /** Retire the PENDING breaks that started while another stream played; returns their ids. */
150
+ skipPendingBreaksDueBy(reason: AdBreakSkipReason): string[];
142
151
  stop(): void;
143
152
  completeBreak(breakId: string): void;
144
153
  /** Bracket a content RELOAD so the new stream's start is not read as a backward seek. `false` also discards the stale previous position. */
@@ -159,6 +168,11 @@ export declare class BreakScheduler {
159
168
  getActiveBreak(): Break | null;
160
169
  /** Rounded seconds until a PENDING break starts, or `null`; refreshes the `upcoming` countdown without polling events. */
161
170
  getSecondsUntilBreak(breakId: string): number | null;
171
+ /** The PENDING break starting soonest (ties: smaller id) with the seconds until it; null when none has a known start. */
172
+ getNextPendingBreak(): {
173
+ id: string;
174
+ secondsUntil: number;
175
+ } | null;
162
176
  /**
163
177
  * Advance the schedule NOW because a cue reached the playhead: the 250ms tick discovers a start
164
178
  * ~125ms late on average, an adapter reporting PRESENTATION knows the instant. Runs
@@ -178,7 +192,7 @@ export declare class BreakScheduler {
178
192
  /** While true no break may start and consumers must not apply break-driven restrictions (e.g. pause ads). */
179
193
  isAdStartDelayActive(): boolean;
180
194
  private getCurrentTime;
181
- /** Fire the preload callback at most once per break; a `false` return is not ledgered and retries next tick. */
195
+ /** Retry rejected callbacks; an accepted preload fires at most once per break. */
182
196
  private maybePreload;
183
197
  private getBreakStartTime;
184
198
  /**
@@ -1 +1 @@
1
- import{BreakEvent as e,Timebase as t,getBreakEvent as i,getPostRollDelay as a,getPreRollDelay as s,isEventTrigger as r,isPauseBreak as n}from"../types/BreakManifest";import{DEFAULT_BREAK_WARNINGS as d,DEFAULT_PDT_GRACE_SECONDS as l}from"../types/OptiViewAdsConfig";import{PTS_MATCH_TOLERANCE_SECONDS as h}from"./MetadataCueTracker";import{debugLog as o}from"../diagnostics/debugLog";import{createBreakTimingState as k,describeBreakStart as c,diagnoseAnvatoBreakEnd as u,getBreakStartTime as b,getCurrentTime as g,secondsUntilBreak as S}from"./breakTiming";import{RewindRearm as m}from"./RewindRearm";import{BreakState as p,systemClock as B}from"./BreakSchedulerTypes";export{BreakState,systemClock}from"./BreakSchedulerTypes";export const TICK_INTERVAL_MS=250;export class BreakScheduler{constructor(e,t=!1,i={enabled:!1,maxGapSeconds:0},a=B,s={enabled:!1,minBreakDurationSeconds:0},r=null,n=d,h=null,o=l){this.breaks=new Map,this.timing=k(),this.checkInterval=null,this.startDeadline=null,this.nextStartDeadlineMs=null,this.onBreakStart=null,this.onBreakApproaching=null,this.onBreakWarning=null,this.onBreakAborted=null,this.onBreakSkipped=null,this.breakStartsHeld=!1,this.onAdStartDelayElapsed=null,this.adStartDelayElapsedNotified=!1,this.abortNotifiedBreakIds=new Set,this.preloadedBreakIds=new Set,this.breakWarnings=d,this.breakWarningFired=new Map,this.completedBreakIds=new Set,this.unsupportedEventBreakIds=new Set,this.playbackEndedAtMs=null,this.anvatoEnteredDiagnosed=new Set,this.retiredBreaks=new Map,this.minSeenTime=null,this.JOIN_DISCRIMINATOR_SLACK_MS=1e3,this.WINDOW_ESCAPE_GRACE_MS=12e3,this.TIMING_TOLERANCE_MS=5e3,this.PRELOAD_AHEAD_SECONDS=8,this.contentReloading=!1,this.startupSeeking=!1,this.player=e,this.debug=t,this.chainingEnabled=i.enabled,this.maxChainGapMs=1e3*Math.max(0,i.maxGapSeconds),this.tuneInEnabled=s.enabled,this.minBreakDurationMs=1e3*Math.max(0,s.minBreakDurationSeconds),this.clock=a,this.diagnose=r,this.breakWarnings=n,this.cueTracker=h;const c=Math.max(0,o);this.timingDeps={player:this.player,clock:this.clock,cueTracker:this.cueTracker,diagnose:this.diagnose,log:(...e)=>this.log(...e),pdtGraceSeconds:c,pdtGraceMs:1e3*c},this.rewind=new m({breaks:this.breaks,retiredBreaks:this.retiredBreaks,completedBreakIds:this.completedBreakIds,preloadedBreakIds:this.preloadedBreakIds,timing:this.timing,clock:this.clock,log:(...e)=>this.log(...e),getBreakStartTime:e=>this.getBreakStartTime(e)})}claimChain(e){if(!this.chainingEnabled)return;let t=this.breaks.get(e)?.break??null;for(;t;){const e=this.findSuccessor(t);if(!e)break;const i=this.breaks.get(e.id);if(!i||i.state!==p.PENDING)break;i.state=p.CHAINED,this.log(`🔗 Break "${e.id}" claimed as chained after "${t.id}"`),t=e}}getChainedSuccessor(e){if(!this.chainingEnabled)return null;const t=this.breaks.get(e)?.break;return t?this.findSuccessor(t,[p.PENDING,p.CHAINED]):null}activateChainedBreak(e){const t=this.breaks.get(e);t&&(t.state=p.ACTIVE)}findSuccessor(e,t=[p.PENDING]){const i=this.getBreakStartTime(e);if(null===i)return null;const a=i+1e3*(e.duration??0);let s=null;for(const r of this.breaks.values()){if(r.break.id===e.id)continue;if(!t.includes(r.state))continue;const n=this.getBreakStartTime(r.break);if(null===n)continue;if(n<=i)continue;const d=n-a;d>this.maxChainGapMs||(d<-this.TIMING_TOLERANCE_MS||(!s||n<s.start)&&(s={break:r.break,start:n}))}return s?.break??null}updateManifest(e){this.timing.timebase=e.timebase;const t=this.rewind.adStartDelayMs;this.rewind.setAdStartDelayMs(1e3*Math.max(0,e.adStartDelay??0)),t>0&&0===this.rewind.adStartDelayMs&&this.notifyAdStartDelayElapsed();for(const t of e.breaks){if(n(t))continue;if(!this.isSchedulableEvent(t))continue;if(this.completedBreakIds.has(t.id))continue;const e=this.breaks.get(t.id);e?e.state===p.PENDING&&(e.break=t):(this.breaks.set(t.id,{break:t,state:p.PENDING}),this.log(`📅 Break scheduled: "${t.id}" at ${c(t)}`))}const i=new Set(e.breaks.map(e=>e.id));for(const[e,t]of this.breaks)i.has(e)||t.state!==p.ACTIVE||this.abortNotifiedBreakIds.has(e)||(this.abortNotifiedBreakIds.add(e),this.log("Break removed from manifest while active (early return):",e),this.diagnose?.("DA-BREAK-EARLY-RETURN","Active break removed from manifest; returning to content early",{breakId:e}),this.onBreakAborted?.(t.break));this.cleanupBreaks(i)}resetForChannelSwitch(e){const t=this.getActiveBreak();for(const e of[...this.breaks.keys()])e!==t?.id&&this.breaks.delete(e);for(const e of[...this.abortNotifiedBreakIds])e!==t?.id&&this.abortNotifiedBreakIds.delete(e);this.completedBreakIds.clear(),this.retiredBreaks.clear(),this.preloadedBreakIds.clear(),this.breakWarningFired.clear(),this.unsupportedEventBreakIds.clear(),this.anvatoEnteredDiagnosed.clear(),this.timing.anvatoPendingDiagnosed.clear(),this.timing.anvatoMatchedDiagnosed.clear(),this.timing.anvatoEndDiagnosed.clear(),this.playbackEndedAtMs=null,this.rewind.beginChannel();const i=t?{...e,breaks:e.breaks.filter(e=>e.id!==t.id)}:e;this.updateManifest(i)}isSchedulableEvent(t){return!r(t.start)||t.start.event===e.START||t.start.event===e.END||(this.unsupportedEventBreakIds.has(t.id)||(this.unsupportedEventBreakIds.add(t.id),this.log(`⚠️ Break ignored: "${t.id}" (unsupported event trigger "${t.start.event}")`),this.diagnose?.("DA-BREAK-EVENT-UNSUPPORTED","Break triggered by an event this SDK version does not schedule; the break was ignored",{breakId:t.id,event:t.start.event})),!1)}markPlaybackStart(){null!==this.playbackEndedAtMs&&(this.playbackEndedAtMs=null,this.log("Content replayed; pending post-rolls cancelled")),this.rewind.playbackStarted||(this.rewind.markPlaybackStart(this.peekPosition()),this.log("Content playback started at",new Date(this.clock.now()).toISOString()))}setOnAdStartDelayElapsed(e){this.onAdStartDelayElapsed=e}holdBreakStarts(){this.breakStartsHeld=!0}releaseBreakStarts(){this.breakStartsHeld=!1,this.checkBreaks()}areBreakStartsHeld(){return this.breakStartsHeld||this.rewind.isAdStartDelayActive()}notifyAdStartDelayElapsed(){this.adStartDelayElapsedNotified||(this.adStartDelayElapsedNotified=!0,this.onAdStartDelayElapsed?.())}markPlaybackEnd(){null===this.playbackEndedAtMs&&(this.playbackEndedAtMs=this.clock.now(),this.log("Content playback ended at",new Date(this.playbackEndedAtMs).toISOString()))}start(e,t,i,a,s){this.onBreakStart=e,this.onBreakApproaching=t??null,this.onBreakWarning=i??null,this.onBreakAborted=a??null,this.onBreakSkipped=s??null,this.checkInterval&&clearInterval(this.checkInterval),this.checkInterval=setInterval(()=>{this.tick()},250),this.log("Scheduler started")}tick(){this.checkBreaks(),null!==this.nextStartDeadlineMs&&this.armStartDeadline(this.nextStartDeadlineMs)}armStartDeadline(e){null!==this.checkInterval&&null===this.startDeadline&&(this.startDeadline=setTimeout(()=>{this.startDeadline=null,this.tick()},Math.max(1,Math.ceil(e))))}clearStartDeadline(){this.startDeadline&&clearTimeout(this.startDeadline),this.startDeadline=null}stop(){this.checkInterval&&(clearInterval(this.checkInterval),this.checkInterval=null),this.clearStartDeadline(),this.onBreakStart=null,this.onBreakSkipped=null,this.log("Scheduler stopped")}completeBreak(e){this.completedBreakIds.add(e),this.breakWarningFired.delete(e),this.abortNotifiedBreakIds.delete(e);const t=this.breaks.get(e);t&&(this.log("Break completed and removed:",e),null===i(t.break)&&null===this.cueTracker&&this.retiredBreaks.set(e,t.break),this.breaks.delete(e),this.preloadedBreakIds.delete(e))}setContentReloading(e){this.contentReloading=e,this.rewind.setContentReloading(e||this.startupSeeking)}setStartupSeeking(e){this.startupSeeking=e,this.rewind.setContentReloading(e||this.contentReloading)}notifyResumeSeek(e,t){this.rewind.notifyResumeSeek(e,t)}notifyViewerSeek(){this.rewind.notifyViewerSeek()}skipBreak(e,t){e.state=p.SKIPPED,this.completedBreakIds.add(e.break.id),this.onBreakSkipped?.(e.break,t)}cleanupBreaks(e){const t=[];for(const[i,a]of this.breaks)a.state!==p.COMPLETED&&a.state!==p.SKIPPED?e.has(i)||a.state===p.ACTIVE||(this.skipBreak(a,"removed"),t.push(i)):t.push(i);for(const e of t)this.breaks.delete(e),this.preloadedBreakIds.delete(e),this.breakWarningFired.delete(e)}getActiveBreak(){for(const e of this.breaks.values())if(e.state===p.ACTIVE)return e.break;return null}getSecondsUntilBreak(e){const t=this.breaks.get(e);if(!t||t.state!==p.PENDING)return null;const i=this.getCurrentTime();if(null===i)return null;const a=this.getBreakStartTime(t.break);return null===a?null:S(a,i)}notifyCuePresented(e){if(null===this.cueTracker||this.timing.timebase!==t.PTS)return null;if(!Number.isFinite(e))return null;let i=null;for(const t of this.breaks.values())if(t.state===p.PENDING&&"number"==typeof t.break.start&&!(Math.abs(t.break.start-e)>h)){i=t.break.id;break}if(null===i)return null;const a=this.player.currentTime;this.checkBreaks();const s=this.breaks.get(i);return s&&s.state===p.ACTIVE?this.anvatoEnteredDiagnosed.has(i)?null:(this.anvatoEnteredDiagnosed.add(i),this.log(`🎯 Break "${i}" armed by cue presentation (pts ${e})`),this.diagnose?.("DA-ANVATO-CUE-ENTERED",`Break "${i}" started from the presented cue (pts ${e}) rather than the scheduler tick.`,{breakId:i,pts:e,mediaTimeSec:a}),i):null}checkBreaks(){if(this.nextStartDeadlineMs=null,this.startupSeeking)return;const e=this.getCurrentTime();this.rewind.trackPlayedTime(e),this.rewind.adStartDelayMs>0&&!this.rewind.isAdStartDelayActive()&&this.notifyAdStartDelayElapsed();const i=this.areBreakStartsHeld();null!==e&&(this.minSeenTime=null===this.minSeenTime?e:Math.min(this.minSeenTime,e),this.rewind.rearmOnRewind(e));for(const r of this.breaks.values()){if(r.state===p.ACTIVE&&this.timing.timebase===t.WALLCLOCK&&null!==e&&!this.abortNotifiedBreakIds.has(r.break.id)){const t=this.getBreakStartTime(r.break),i=1e3*(r.break.duration??0);null!==t&&i>0&&e>t+i+this.WINDOW_ESCAPE_GRACE_MS&&(this.abortNotifiedBreakIds.add(r.break.id),this.log(`⚠️ Break window escaped: "${r.break.id}" (ended ${Math.round((e-t-i)/1e3)}s ago on the stream clock)`),this.diagnose?.("DA-BREAK-WINDOW-ESCAPED","Active break outlived its wallclock window; returning to content",{breakId:r.break.id,overrunSec:Math.round((e-t-i)/100)/10}),this.onBreakAborted?.(r.break));continue}if(r.state===p.ACTIVE&&this.timing.timebase===t.PTS&&u(this.timing,this.timingDeps,r.break),r.state!==p.PENDING)continue;const n=s(r.break);if(null!==n){this.checkPreRollBreak(r,n,i);continue}const d=a(r.break);if(null!==d){this.checkPostRollBreak(r,d,i);continue}if(null===e)continue;if(i)continue;null!==this.cueTracker&&this.timing.timebase===t.PTS&&this.maybePreload(r.break,"manifest arrival — Anvato flow");const l=this.getBreakStartTime(r.break);if(null===l)continue;const h=e-l,o=Math.round(-h/1e3);if(this.timing.timebase===t.WALLCLOCK&&h<0&&-h<=250&&(this.nextStartDeadlineMs=Math.min(this.nextStartDeadlineMs??1/0,-h)),this.checkWarnings(r.break,o),o>0&&o<=15&&o%2==0&&this.log(`⏱️ Break "${r.break.id}" starts in ${o}s`),o>0&&o<=this.PRELOAD_AHEAD_SECONDS&&this.maybePreload(r.break,`${o}s ahead`),h>=0&&h<=this.TIMING_TOLERANCE_MS){const e=Math.max(0,h),t=1e3*(r.break.duration??0)-e;if(t<=0){this.log(`⚠️ Break missed: "${r.break.id}" (ended ${Math.round(-t/1e3)}s ago, inside the start tolerance)`),this.skipBreak(r,t>0&&this.tuneInEnabled?"insufficient-remaining":"late"),null===this.cueTracker&&this.breaks.get(r.break.id)===r&&this.retiredBreaks.set(r.break.id,r.break);break}if(null!==this.minSeenTime&&l<this.minSeenTime-this.JOIN_DISCRIMINATOR_SLACK_MS&&(!this.tuneInEnabled||t<this.minBreakDurationMs)){this.log(`⚠️ Break missed: "${r.break.id}" (joined in progress inside the start tolerance, ${Math.round(t/1e3)}s remaining < tune-in rules)`),this.skipBreak(r,t>0&&this.tuneInEnabled?"insufficient-remaining":"late"),null===this.cueTracker&&this.breaks.get(r.break.id)===r&&this.retiredBreaks.set(r.break.id,r.break);break}this.log(`🎬 BREAK TRIGGERED: "${r.break.id}"`),r.state=p.ACTIVE,this.onBreakStart&&this.onBreakStart(r.break,{tuneIn:!1,elapsedSec:e/1e3,remainingSec:t/1e3});break}if(h>this.TIMING_TOLERANCE_MS){const t=l+1e3*(r.break.duration??0)-e;if(this.tuneInEnabled&&t>=this.minBreakDurationMs&&t>0){this.log(`🎬 TUNE-IN BREAK TRIGGERED: "${r.break.id}" (${Math.round(t/1e3)}s remaining)`),r.state=p.ACTIVE,this.onBreakStart&&this.onBreakStart(r.break,{tuneIn:!0,elapsedSec:h/1e3,remainingSec:t/1e3});break}this.log(`⚠️ Break missed: "${r.break.id}" (was ${Math.round(h/1e3)}s ago)`),this.skipBreak(r,t>0&&this.tuneInEnabled?"insufficient-remaining":"late"),null===this.cueTracker&&this.breaks.get(r.break.id)===r&&this.retiredBreaks.set(r.break.id,r.break)}}}checkPreRollBreak(e,t,i){if(!this.rewind.playbackStarted)return;const a=1e3*t;if(this.rewind.adStartDelayMs>0&&a<this.rewind.adStartDelayMs)return this.log(`⚠️ Pre-roll skipped: "${e.break.id}" (delay ${t}s < adStartDelay ${this.rewind.adStartDelayMs/1e3}s)`),void this.skipBreak(e,"start-delay");const s=this.rewind.channelPlayedMs,r=Math.round((a-s)/1e3);r>0&&r<=15&&r%2==0&&this.log(`⏱️ Pre-roll "${e.break.id}" starts in ${r}s`),r>0&&r<=this.PRELOAD_AHEAD_SECONDS&&this.maybePreload(e.break,`pre-roll, ${r}s ahead`),s>=a&&!i&&(this.log(`🎬 PRE-ROLL TRIGGERED: "${e.break.id}"${t>0?` (after ${t}s delay)`:""}`),e.state=p.ACTIVE,this.onBreakStart&&this.onBreakStart(e.break,{tuneIn:!1,elapsedSec:0,remainingSec:e.break.duration??0}))}checkPostRollBreak(e,t,i){if(null===this.playbackEndedAtMs)return;const a=1e3*t,s=this.clock.now()-this.playbackEndedAtMs,r=Math.round((a-s)/1e3);r>0&&r<=this.PRELOAD_AHEAD_SECONDS&&this.maybePreload(e.break,`post-roll, ${r}s ahead`),s>=a&&!i&&(this.log(`🎬 POST-ROLL TRIGGERED: "${e.break.id}"${t>0?` (after ${t}s delay)`:""}`),e.state=p.ACTIVE,this.onBreakStart&&this.onBreakStart(e.break,{tuneIn:!1,elapsedSec:0,remainingSec:e.break.duration??0}))}isAdStartDelayActive(){return this.areBreakStartsHeld()}getCurrentTime(){return g(this.timing,this.timingDeps)}maybePreload(e,t){if(this.preloadedBreakIds.has(e.id)||!this.onBreakApproaching)return;this.log(`⏳ Preloading break "${e.id}" (${t})`);!1!==this.onBreakApproaching(e)&&this.preloadedBreakIds.add(e.id)}getBreakStartTime(e){return r(e.start)?null:b(this.timing,this.timingDeps,e)}peekPosition(){if(this.timing.timebase!==t.WALLCLOCK)return 1e3*this.player.currentTime;const e=this.player.programDateTime?.getTime();return void 0===e||isNaN(e)?null:e}destroy(){this.stop(),this.breaks.clear(),this.preloadedBreakIds.clear(),this.completedBreakIds.clear(),this.retiredBreaks.clear(),this.minSeenTime=null,this.breakStartsHeld=!1,this.onAdStartDelayElapsed=null,this.adStartDelayElapsedNotified=!1,this.unsupportedEventBreakIds.clear(),this.playbackEndedAtMs=null,this.rewind.reset(),this.breakWarningFired.clear(),this.abortNotifiedBreakIds.clear(),this.timing.pdtMissingWarned=!1,this.timing.pdtSeen=!1,this.timing.firstPdtAttemptMs=null,this.timing.playbackStartedAtMs=null,this.timing.anvatoPendingDiagnosed.clear(),this.timing.anvatoMatchedDiagnosed.clear(),this.timing.anvatoEndDiagnosed.clear(),this.cueTracker?.clear()}checkWarnings(e,t){if(t<=0||0===this.breakWarnings.seconds.length)return;let i=this.breakWarningFired.get(e.id);i||(i=new Set,this.breakWarningFired.set(e.id,i));for(const a of this.breakWarnings.seconds)i.has(a)||t<=a&&(i.add(a),this.log(`Break warning for "${e.id}" (${t}s <= ${a}s)`),this.onBreakWarning?.(e,a,t))}log(...e){this.debug&&o("[OptiViewAds:BreakScheduler]",...e)}}
1
+ import{BreakEvent as e,Timebase as t,getBreakEvent as a,getPostRollDelay as i,getPreRollDelay as r,isEventTrigger as s,isPauseBreak as n}from"../types/BreakManifest";import{DEFAULT_BREAK_WARNINGS as d,DEFAULT_PDT_GRACE_SECONDS as h}from"../types/OptiViewAdsConfig";import{PTS_MATCH_TOLERANCE_SECONDS as l}from"./MetadataCueTracker";import{debugLog as o}from"../diagnostics/debugLog";import{createBreakTimingState as k,describeBreakStart as c,diagnoseAnvatoBreakEnd as u,getBreakStartTime as b,getCurrentTime as g,secondsUntilBreak as S}from"./breakTiming";import{BreakPreloadLedger as m}from"./BreakPreloadLedger";import{skipBreaksStartedBy as p,soonestBreak as B}from"./ssbStream";import{RewindRearm as E}from"./RewindRearm";import{BreakChain as f}from"./BreakChain";import{BreakState as D,systemClock as I}from"./BreakSchedulerTypes";export{BreakState,systemClock}from"./BreakSchedulerTypes";export const TICK_INTERVAL_MS=250;export class BreakScheduler{constructor(e,t=!1,a={enabled:!1,maxGapSeconds:0},i=I,r={enabled:!1,minBreakDurationSeconds:0},s=null,n=d,l=null,o=h){this.breaks=new Map,this.timing=k(),this.checkInterval=null,this.startDeadline=null,this.nextStartDeadlineMs=null,this.onBreakStart=null,this.onBreakApproaching=null,this.onBreakWarning=null,this.onBreakAborted=null,this.onBreakSkipped=null,this.beforeBreakCheck=null,this.breakStartsHeld=!1,this.preloadLedger=new m,this.onAdStartDelayElapsed=null,this.adStartDelayElapsedNotified=!1,this.abortNotifiedBreakIds=new Set,this.breakWarnings=d,this.breakWarningFired=new Map,this.completedBreakIds=new Set,this.unsupportedEventBreakIds=new Set,this.playbackEndedAtMs=null,this.anvatoEnteredDiagnosed=new Set,this.retiredBreaks=new Map,this.minSeenTime=null,this.JOIN_DISCRIMINATOR_SLACK_MS=1e3,this.WINDOW_ESCAPE_GRACE_MS=12e3,this.TIMING_TOLERANCE_MS=5e3,this.PRELOAD_AHEAD_SECONDS=8,this.contentReloading=!1,this.startupSeeking=!1,this.player=e,this.debug=t,this.chain=new f({breaks:this.breaks,enabled:a.enabled,maxGapMs:1e3*Math.max(0,a.maxGapSeconds),toleranceMs:this.TIMING_TOLERANCE_MS,getBreakStartTime:e=>this.getBreakStartTime(e),log:(...e)=>this.log(...e)}),this.tuneInEnabled=r.enabled,this.minBreakDurationMs=1e3*Math.max(0,r.minBreakDurationSeconds),this.clock=i,this.diagnose=s,this.breakWarnings=n,this.cueTracker=l;const c=Math.max(0,o);this.timingDeps={player:this.player,clock:this.clock,cueTracker:this.cueTracker,diagnose:this.diagnose,log:(...e)=>this.log(...e),pdtGraceSeconds:c,pdtGraceMs:1e3*c},this.rewind=new E({breaks:this.breaks,retiredBreaks:this.retiredBreaks,completedBreakIds:this.completedBreakIds,preloadLedger:this.preloadLedger,timing:this.timing,clock:this.clock,log:(...e)=>this.log(...e),getBreakStartTime:e=>this.getBreakStartTime(e)})}claimChain(e){this.chain.claim(e)}getChainedSuccessor(e){return this.chain.successorOf(e)}activateChainedBreak(e){this.chain.activate(e)}setOnChainSuccessorChanged(e){this.chain.setOnSuccessorChanged(e)}updateManifest(e){this.timing.timebase=e.timebase;const t=this.rewind.adStartDelayMs;this.rewind.setAdStartDelayMs(1e3*Math.max(0,e.adStartDelay??0)),t>0&&0===this.rewind.adStartDelayMs&&this.notifyAdStartDelayElapsed();for(const t of e.breaks){if(n(t))continue;if(!this.isSchedulableEvent(t))continue;if(this.completedBreakIds.has(t.id))continue;const e=this.breaks.get(t.id);e?e.state!==D.PENDING&&e.state!==D.CHAINED||(e.break=t):(this.breaks.set(t.id,{break:t,state:D.PENDING}),this.log(`📅 Break scheduled: "${t.id}" at ${c(t)}`))}const a=new Set(e.breaks.map(e=>e.id));for(const[e,t]of this.breaks)a.has(e)||t.state!==D.ACTIVE||this.abortNotifiedBreakIds.has(e)||(this.abortNotifiedBreakIds.add(e),this.log("Break removed from manifest while active (early return):",e),this.diagnose?.("DA-BREAK-EARLY-RETURN","Active break removed from manifest; returning to content early",{breakId:e}),this.onBreakAborted?.(t.break));this.cleanupBreaks(a);const i=this.getActiveBreak();i&&!this.abortNotifiedBreakIds.has(i.id)&&this.chain.refresh(i)}applyEventBoundary(e){const t=this.rewind.playbackStarted;this.updateManifest(t?{...e,adStartDelay:0}:e),t&&this.rewind.ignoreAdStartDelay()}setEventBoundaryCheck(e){this.beforeBreakCheck=e}resetForChannelSwitch(e){const t=this.getActiveBreak();for(const e of[...this.breaks.keys()])e!==t?.id&&this.breaks.delete(e);for(const e of[...this.abortNotifiedBreakIds])e!==t?.id&&this.abortNotifiedBreakIds.delete(e);this.completedBreakIds.clear(),this.retiredBreaks.clear(),this.preloadLedger.clear(),this.breakWarningFired.clear(),this.unsupportedEventBreakIds.clear(),this.anvatoEnteredDiagnosed.clear(),this.timing.anvatoPendingDiagnosed.clear(),this.timing.anvatoMatchedDiagnosed.clear(),this.timing.anvatoEndDiagnosed.clear(),this.playbackEndedAtMs=null,this.rewind.beginChannel();const a=t?{...e,breaks:e.breaks.filter(e=>e.id!==t.id)}:e;this.updateManifest(a)}isSchedulableEvent(t){return!s(t.start)||t.start.event===e.START||t.start.event===e.END||(this.unsupportedEventBreakIds.has(t.id)||(this.unsupportedEventBreakIds.add(t.id),this.log(`⚠️ Break ignored: "${t.id}" (unsupported event trigger "${t.start.event}")`),this.diagnose?.("DA-BREAK-EVENT-UNSUPPORTED","Break triggered by an event this SDK version does not schedule; the break was ignored",{breakId:t.id,event:t.start.event})),!1)}markPlaybackStart(){null!==this.playbackEndedAtMs&&(this.playbackEndedAtMs=null,this.log("Content replayed; pending post-rolls cancelled")),this.rewind.playbackStarted||(this.rewind.markPlaybackStart(this.peekPosition()),this.log("Content playback started at",new Date(this.clock.now()).toISOString()))}setOnAdStartDelayElapsed(e){this.onAdStartDelayElapsed=e}holdBreakStarts(){this.breakStartsHeld=!0}releaseBreakStarts(){const e=this.breakStartsHeld;this.breakStartsHeld=!1,this.preloadLedger.checkAtBreakStart(e,()=>this.checkBreaks())}areBreakStartsHeld(){return this.breakStartsHeld||this.rewind.isAdStartDelayActive()}notifyAdStartDelayElapsed(){this.adStartDelayElapsedNotified||(this.adStartDelayElapsedNotified=!0,this.onAdStartDelayElapsed?.())}markPlaybackEnd(){null===this.playbackEndedAtMs&&(this.playbackEndedAtMs=this.clock.now(),this.log("Content playback ended at",new Date(this.playbackEndedAtMs).toISOString()))}start(e,t,a,i,r){this.onBreakStart=e,this.onBreakApproaching=t??null,this.onBreakWarning=a??null,this.onBreakAborted=i??null,this.onBreakSkipped=r??null,this.checkInterval&&clearInterval(this.checkInterval),this.checkInterval=setInterval(()=>{this.tick()},250),this.log("Scheduler started")}tick(){!1!==this.beforeBreakCheck?.(this.getCurrentTime())&&(this.checkBreaks(),null!==this.nextStartDeadlineMs&&this.armStartDeadline(this.nextStartDeadlineMs))}armStartDeadline(e){null!==this.checkInterval&&null===this.startDeadline&&(this.startDeadline=setTimeout(()=>{this.startDeadline=null,this.tick()},Math.max(1,Math.ceil(e))))}clearStartDeadline(){this.startDeadline&&clearTimeout(this.startDeadline),this.startDeadline=null}suspend(){this.checkInterval&&clearInterval(this.checkInterval),this.checkInterval=null,this.clearStartDeadline()}resume(){!this.checkInterval&&this.onBreakStart&&(this.checkInterval=setInterval(()=>this.tick(),250))}skipPendingBreaksDueBy(e){return p(this.getCurrentTime(),[...this.breaks.values()],e=>e.state===D.PENDING?this.getBreakStartTime(e.break):null,t=>this.skipBreak(t,e))}stop(){this.checkInterval&&(clearInterval(this.checkInterval),this.checkInterval=null),this.clearStartDeadline(),this.onBreakStart=null,this.onBreakSkipped=null,this.log("Scheduler stopped")}completeBreak(e){this.completedBreakIds.add(e),this.breakWarningFired.delete(e),this.abortNotifiedBreakIds.delete(e);const t=this.breaks.get(e);t&&(this.log("Break completed and removed:",e),null===a(t.break)&&null===this.cueTracker&&this.retiredBreaks.set(e,t.break),this.breaks.delete(e),this.preloadLedger.delete(e))}setContentReloading(e){this.contentReloading=e,this.rewind.setContentReloading(e||this.startupSeeking)}setStartupSeeking(e){this.startupSeeking=e,this.rewind.setContentReloading(e||this.contentReloading)}notifyResumeSeek(e,t){this.rewind.notifyResumeSeek(e,t)}notifyViewerSeek(){this.rewind.notifyViewerSeek()}skipBreak(e,t){e.state=D.SKIPPED,this.completedBreakIds.add(e.break.id),this.onBreakSkipped?.(e.break,t)}cleanupBreaks(e){const t=[];for(const[a,i]of this.breaks)i.state!==D.COMPLETED&&i.state!==D.SKIPPED?e.has(a)||i.state===D.ACTIVE||(this.skipBreak(i,"removed"),t.push(a)):t.push(a);for(const e of t)this.breaks.delete(e),this.preloadLedger.delete(e),this.breakWarningFired.delete(e)}getActiveBreak(){for(const e of this.breaks.values())if(e.state===D.ACTIVE)return e.break;return null}getSecondsUntilBreak(e){const t=this.breaks.get(e);if(!t||t.state!==D.PENDING)return null;const a=this.getCurrentTime();if(null===a)return null;const i=this.getBreakStartTime(t.break);return null===i?null:S(i,a)}getNextPendingBreak(){return B([...this.breaks.keys()],e=>this.getSecondsUntilBreak(e))}notifyCuePresented(e){if(null===this.cueTracker||this.timing.timebase!==t.PTS)return null;if(!Number.isFinite(e))return null;let a=null;for(const t of this.breaks.values())if(t.state===D.PENDING&&"number"==typeof t.break.start&&!(Math.abs(t.break.start-e)>l)){a=t.break.id;break}if(null===a)return null;const i=this.player.currentTime;this.checkBreaks();const r=this.breaks.get(a);return r&&r.state===D.ACTIVE?this.anvatoEnteredDiagnosed.has(a)?null:(this.anvatoEnteredDiagnosed.add(a),this.log(`🎯 Break "${a}" armed by cue presentation (pts ${e})`),this.diagnose?.("DA-ANVATO-CUE-ENTERED",`Break "${a}" started from the presented cue (pts ${e}) rather than the scheduler tick.`,{breakId:a,pts:e,mediaTimeSec:i}),a):null}checkBreaks(){if(this.nextStartDeadlineMs=null,this.startupSeeking)return;const e=this.getCurrentTime();this.rewind.trackPlayedTime(e),this.rewind.adStartDelayMs>0&&!this.rewind.isAdStartDelayActive()&&this.notifyAdStartDelayElapsed();const a=this.areBreakStartsHeld();null!==e&&(this.minSeenTime=null===this.minSeenTime?e:Math.min(this.minSeenTime,e),this.rewind.rearmOnRewind(e));for(const s of this.breaks.values()){if(s.state===D.ACTIVE&&this.timing.timebase===t.WALLCLOCK&&null!==e&&!this.abortNotifiedBreakIds.has(s.break.id)){const t=this.getBreakStartTime(s.break),a=1e3*(s.break.duration??0);null!==t&&a>0&&e>t+a+this.WINDOW_ESCAPE_GRACE_MS&&(this.abortNotifiedBreakIds.add(s.break.id),this.log(`⚠️ Break window escaped: "${s.break.id}" (ended ${Math.round((e-t-a)/1e3)}s ago on the stream clock)`),this.diagnose?.("DA-BREAK-WINDOW-ESCAPED","Active break outlived its wallclock window; returning to content",{breakId:s.break.id,overrunSec:Math.round((e-t-a)/100)/10}),this.onBreakAborted?.(s.break));continue}if(s.state===D.ACTIVE&&this.timing.timebase===t.PTS&&u(this.timing,this.timingDeps,s.break),s.state!==D.PENDING)continue;const n=r(s.break);if(null!==n){this.checkPreRollBreak(s,n,a);continue}const d=i(s.break);if(null!==d){this.checkPostRollBreak(s,d,a);continue}if(null===e)continue;if(a)continue;null!==this.cueTracker&&this.timing.timebase===t.PTS&&this.maybePreload(s.break,"manifest arrival — Anvato flow");const h=this.getBreakStartTime(s.break);if(null===h)continue;const l=e-h,o=Math.round(-l/1e3);if(this.timing.timebase===t.WALLCLOCK&&l<0&&-l<=250&&(this.nextStartDeadlineMs=Math.min(this.nextStartDeadlineMs??1/0,-l)),this.checkWarnings(s.break,o),o>0&&o<=15&&o%2==0&&this.log(`⏱️ Break "${s.break.id}" starts in ${o}s`),o>0&&o<=this.PRELOAD_AHEAD_SECONDS&&this.maybePreload(s.break,`${o}s ahead`),l>=0&&l<=this.TIMING_TOLERANCE_MS){const e=Math.max(0,l),t=1e3*(s.break.duration??0)-e;if(t<=0){this.log(`⚠️ Break missed: "${s.break.id}" (ended ${Math.round(-t/1e3)}s ago, inside the start tolerance)`),this.skipBreak(s,t>0&&this.tuneInEnabled?"insufficient-remaining":"late"),null===this.cueTracker&&this.breaks.get(s.break.id)===s&&this.retiredBreaks.set(s.break.id,s.break);break}if(null!==this.minSeenTime&&h<this.minSeenTime-this.JOIN_DISCRIMINATOR_SLACK_MS&&(!this.tuneInEnabled||t<this.minBreakDurationMs)){this.log(`⚠️ Break missed: "${s.break.id}" (joined in progress inside the start tolerance, ${Math.round(t/1e3)}s remaining < tune-in rules)`),this.skipBreak(s,t>0&&this.tuneInEnabled?"insufficient-remaining":"late"),null===this.cueTracker&&this.breaks.get(s.break.id)===s&&this.retiredBreaks.set(s.break.id,s.break);break}this.preloadLedger.shouldRetryRejectedAtStart(s.break.id)&&this.maybePreload(s.break,"retrying a rejected preload at break start"),this.log(`🎬 BREAK TRIGGERED: "${s.break.id}"`),s.state=D.ACTIVE,this.onBreakStart&&this.onBreakStart(s.break,{tuneIn:!1,elapsedSec:e/1e3,remainingSec:t/1e3});break}if(l>this.TIMING_TOLERANCE_MS){const t=h+1e3*(s.break.duration??0)-e;if(this.tuneInEnabled&&t>=this.minBreakDurationMs&&t>0){this.log(`🎬 TUNE-IN BREAK TRIGGERED: "${s.break.id}" (${Math.round(t/1e3)}s remaining)`),s.state=D.ACTIVE,this.onBreakStart&&this.onBreakStart(s.break,{tuneIn:!0,elapsedSec:l/1e3,remainingSec:t/1e3});break}this.log(`⚠️ Break missed: "${s.break.id}" (was ${Math.round(l/1e3)}s ago)`),this.skipBreak(s,t>0&&this.tuneInEnabled?"insufficient-remaining":"late"),null===this.cueTracker&&this.breaks.get(s.break.id)===s&&this.retiredBreaks.set(s.break.id,s.break)}}}checkPreRollBreak(e,t,a){if(!this.rewind.playbackStarted)return;const i=1e3*t;if(this.rewind.adStartDelayMs>0&&i<this.rewind.adStartDelayMs)return this.log(`⚠️ Pre-roll skipped: "${e.break.id}" (delay ${t}s < adStartDelay ${this.rewind.adStartDelayMs/1e3}s)`),void this.skipBreak(e,"start-delay");const r=this.rewind.channelPlayedMs,s=Math.round((i-r)/1e3);s>0&&s<=15&&s%2==0&&this.log(`⏱️ Pre-roll "${e.break.id}" starts in ${s}s`),s>0&&s<=this.PRELOAD_AHEAD_SECONDS&&this.maybePreload(e.break,`pre-roll, ${s}s ahead`),r>=i&&!a&&(this.log(`🎬 PRE-ROLL TRIGGERED: "${e.break.id}"${t>0?` (after ${t}s delay)`:""}`),e.state=D.ACTIVE,this.onBreakStart&&this.onBreakStart(e.break,{tuneIn:!1,elapsedSec:0,remainingSec:e.break.duration??0}))}checkPostRollBreak(e,t,a){if(null===this.playbackEndedAtMs)return;const i=1e3*t,r=this.clock.now()-this.playbackEndedAtMs,s=Math.round((i-r)/1e3);s>0&&s<=this.PRELOAD_AHEAD_SECONDS&&this.maybePreload(e.break,`post-roll, ${s}s ahead`),r>=i&&!a&&(this.log(`🎬 POST-ROLL TRIGGERED: "${e.break.id}"${t>0?` (after ${t}s delay)`:""}`),e.state=D.ACTIVE,this.onBreakStart&&this.onBreakStart(e.break,{tuneIn:!1,elapsedSec:0,remainingSec:e.break.duration??0}))}isAdStartDelayActive(){return this.areBreakStartsHeld()}getCurrentTime(){return g(this.timing,this.timingDeps)}maybePreload(e,t){if(this.preloadLedger.wasPreloaded(e.id)||!this.onBreakApproaching)return;this.log(`⏳ Preloading break "${e.id}" (${t})`);const a=!1!==this.onBreakApproaching(e);this.preloadLedger.recordAttempt(e.id,a)}getBreakStartTime(e){return s(e.start)?null:b(this.timing,this.timingDeps,e)}peekPosition(){if(this.timing.timebase!==t.WALLCLOCK)return 1e3*this.player.currentTime;const e=this.player.programDateTime?.getTime();return void 0===e||isNaN(e)?null:e}destroy(){this.stop(),this.breaks.clear(),this.preloadLedger.clear(),this.completedBreakIds.clear(),this.retiredBreaks.clear(),this.minSeenTime=null,this.breakStartsHeld=!1,this.onAdStartDelayElapsed=null,this.adStartDelayElapsedNotified=!1,this.unsupportedEventBreakIds.clear(),this.playbackEndedAtMs=null,this.rewind.reset(),this.breakWarningFired.clear(),this.abortNotifiedBreakIds.clear(),this.timing.pdtMissingWarned=!1,this.timing.pdtSeen=!1,this.timing.firstPdtAttemptMs=null,this.timing.playbackStartedAtMs=null,this.timing.anvatoPendingDiagnosed.clear(),this.timing.anvatoMatchedDiagnosed.clear(),this.timing.anvatoEndDiagnosed.clear(),this.cueTracker?.clear()}checkWarnings(e,t){if(t<=0||0===this.breakWarnings.seconds.length)return;let a=this.breakWarningFired.get(e.id);a||(a=new Set,this.breakWarningFired.set(e.id,a));for(const i of this.breakWarnings.seconds)a.has(i)||t<=i&&(a.add(i),this.log(`Break warning for "${e.id}" (${t}s <= ${i}s)`),this.onBreakWarning?.(e,i,t))}log(...e){this.debug&&o("[OptiViewAds:BreakScheduler]",...e)}}