@dolby-optiview/ads-sdk-core 1.0.0 → 1.1.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 (77) hide show
  1. package/README.md +68 -0
  2. package/dist/OptiViewAds.d.ts +1 -0
  3. package/dist/OptiViewAds.js +1 -1
  4. package/dist/diagnostics/recordEventTimeline.d.ts +3 -0
  5. package/dist/diagnostics/recordEventTimeline.js +1 -0
  6. package/dist/generated/diagnosticCodes.d.ts +96 -1
  7. package/dist/generated/diagnosticCodes.js +1 -1
  8. package/dist/generated/version.d.ts +1 -1
  9. package/dist/generated/version.js +1 -1
  10. package/dist/index.d.ts +1 -1
  11. package/dist/interfaces/PlayerAdapter.d.ts +33 -0
  12. package/dist/internal/index.d.ts +1 -1
  13. package/dist/internal/index.js +1 -1
  14. package/dist/services/AdPlayerController.d.ts +3 -0
  15. package/dist/services/AdPlayerController.js +1 -1
  16. package/dist/services/BreakScheduler.d.ts +5 -2
  17. package/dist/services/BreakScheduler.js +1 -1
  18. package/dist/services/CmcdAdReporter.d.ts +10 -0
  19. package/dist/services/CmcdAdReporter.js +1 -1
  20. package/dist/services/GamPodAdTracker.d.ts +12 -1
  21. package/dist/services/GamPodAdTracker.js +1 -1
  22. package/dist/services/LensReporter.d.ts +3 -1
  23. package/dist/services/LensReporter.js +1 -1
  24. package/dist/services/LiveStartPushbackController.d.ts +36 -0
  25. package/dist/services/LiveStartPushbackController.js +1 -0
  26. package/dist/services/MetadataCueTracker.d.ts +15 -0
  27. package/dist/services/MetadataCueTracker.js +1 -1
  28. package/dist/services/VastAdManager.js +1 -1
  29. package/dist/services/adPlayer/AdPlayerState.d.ts +11 -0
  30. package/dist/services/adPlayer/AdPlayerState.js +1 -1
  31. package/dist/services/adPlayer/AdPreloader.d.ts +9 -1
  32. package/dist/services/adPlayer/AdPreloader.js +1 -1
  33. package/dist/services/adPlayer/AdStage.d.ts +5 -0
  34. package/dist/services/adPlayer/AdStage.js +1 -1
  35. package/dist/services/adPlayer/AssetPlayback.d.ts +15 -1
  36. package/dist/services/adPlayer/AssetPlayback.js +1 -1
  37. package/dist/services/adPlayer/ContentResumeCoordinator.d.ts +39 -0
  38. package/dist/services/adPlayer/ContentResumeCoordinator.js +1 -1
  39. package/dist/services/adPlayer/GamPodBridge.d.ts +7 -0
  40. package/dist/services/adPlayer/GamPodBridge.js +1 -1
  41. package/dist/services/adPlayer/PreparedPresentation.d.ts +32 -0
  42. package/dist/services/adPlayer/PreparedPresentation.js +1 -0
  43. package/dist/services/adPlayer/SharedElementBreakPlayer.d.ts +8 -0
  44. package/dist/services/adPlayer/SharedElementBreakPlayer.js +1 -1
  45. package/dist/services/adPlayer/classifyMediaError.d.ts +9 -0
  46. package/dist/services/adPlayer/classifyMediaError.js +1 -0
  47. package/dist/services/adPlayer/liveEdge.d.ts +29 -0
  48. package/dist/services/adPlayer/liveEdge.js +1 -1
  49. package/dist/services/anvatoCueRuntime.d.ts +7 -0
  50. package/dist/services/anvatoCueRuntime.js +1 -1
  51. package/dist/services/classifyImaAdError.d.ts +6 -0
  52. package/dist/services/classifyImaAdError.js +1 -0
  53. package/dist/services/html5ImaErrorNames.d.ts +2 -0
  54. package/dist/services/html5ImaErrorNames.js +1 -0
  55. package/dist/services/lensBackend.d.ts +1 -1
  56. package/dist/services/resolveLiveStartPushback.d.ts +1 -0
  57. package/dist/services/resolveLiveStartPushback.js +1 -0
  58. package/dist/session/AdBreakStatusTracker.js +1 -1
  59. package/dist/session/BreakLifecycle.js +1 -1
  60. package/dist/session/SessionStarter.d.ts +1 -0
  61. package/dist/session/SessionStarter.js +1 -1
  62. package/dist/types/AdError.d.ts +12 -0
  63. package/dist/types/AdError.js +1 -0
  64. package/dist/types/OptiViewAdsConfig.d.ts +17 -0
  65. package/dist/types/VendorAdError.d.ts +6 -5
  66. package/dist/types/VendorAdError.js +1 -1
  67. package/dist/vendor/lens-sdk/THIRD-PARTY-NOTICES.txt +1 -1
  68. package/dist/vendor/lens-sdk/index.d.ts +1 -1
  69. package/dist/vendor/lens-sdk/index.js +1 -1
  70. package/dist/vendor/lens-sdk/lens-core-uGZfZpQy.js +1 -0
  71. package/dist/vendor/lens-sdk/lens.d.ts +2 -0
  72. package/dist/vendor/lens-sdk/teardown.d.ts +13 -0
  73. package/dist/vendor/lens-sdk/transport.d.ts +11 -2
  74. package/dist/vendor/lens-sdk/types.d.ts +56 -5
  75. package/dist/vendor/lens-sdk/validation.d.ts +4 -1
  76. package/package.json +1 -1
  77. package/dist/vendor/lens-sdk/lens-core-Q5lbQGMC.js +0 -1
@@ -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 b}from"./adPlayer/BreakSequenceDriver";import{VariantSelector as E}from"./adPlayer/VariantSelector";import{AdPlayerEvents as F}from"./adPlayer/AdPlayerEvents";import{armPlaybackCutoff as T,clearPlaybackCutoff as M}from"./adPlayer/PlaybackCutoff";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.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.state.contentPositionAtBreakStart=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 E(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),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=>this.loadWithinBudget(e),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 b(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}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)}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.state.contentPositionAtBreakStart=this.contentPlayer.currentTime,this.resumeCoordinator.resetBreakResumeState(),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 Error("Break cut before first frame rendered")):this.sequencer&&this.currentBreak&&this.isPlaying?(this.callbacks?.onBreakCut?.(this.currentBreak),this.drive(this.sequencer.onOutcome({kind:"break-elapsed"}))):this.endBreak()},()=>this.onAdError(new Error("Ad playback startup timed out")))}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 Error(`Break ${a.id} has no playable variant (${e})`)),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){return this.preloader.loadWithinBudget(e)}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.resumeGeneration++,g(this.state),this.sharedElementPlayer.beginSharedActivity()){if(this.adStage.destroy(),this.resumeCoordinator.cancelResumeLandedProbe(),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.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,this.playerContainerCreatedBySDK)for(;this.playerContainer.firstChild;)this.container.appendChild(this.playerContainer.firstChild);else this.container.appendChild(this.playerContainer),this.playerContainer.style.cssText=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),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 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 b}from"./adPlayer/BreakSequenceDriver";import{VariantSelector as E}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 E(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 b(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)}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)}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.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;
@@ -168,7 +168,10 @@ export declare class BreakScheduler {
168
168
  stop(): void;
169
169
  completeBreak(breakId: string): void;
170
170
  /** Bracket a content RELOAD so the new stream's start is not read as a backward seek. `false` also discards the stale previous position. */
171
+ private contentReloading;
172
+ private startupSeeking;
171
173
  setContentReloading(active: boolean): void;
174
+ setStartupSeeking(active: boolean): void;
172
175
  /**
173
176
  * The runtime is about to resume-seek content to `targetSeconds` after a break ending at
174
177
  * `breakEndSeconds`. Arms a guard so the small backward step is not read as a DVR seek-back;
@@ -184,8 +187,8 @@ export declare class BreakScheduler {
184
187
  /**
185
188
  * Advance the schedule NOW because a cue reached the playhead: the 250ms tick discovers a start
186
189
  * ~125ms late on average, an adapter reporting PRESENTATION knows the instant. Runs
187
- * {@link checkBreaks} so every tick guard applies unchanged. A cue cannot present before the
188
- * playhead reaches it, so this can only fire on time, never early.
190
+ * {@link checkBreaks} so every tick guard applies unchanged, including the timing check against
191
+ * the tracker's media time: a segment-anchored emsg cue presenting at its segment start is ignored.
189
192
  * Returns the id of the break this armed, or null.
190
193
  */
191
194
  notifyCuePresented(pts: number): string | null;
@@ -1 +1 @@
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 o}from"../types/OptiViewAdsConfig";import{PTS_MATCH_TOLERANCE_SECONDS as l}from"./MetadataCueTracker";import{debugLog as h}from"../diagnostics/debugLog";import{createBreakTimingState as k,getBreakStartTime as c,getCurrentTime as u,secondsUntilBreak as b}from"./breakTiming";import{RewindRearm as g}from"./RewindRearm";export const systemClock={now:()=>Date.now()};export var BreakState;!function(e){e.PENDING="pending",e.CHAINED="chained",e.ACTIVE="active",e.COMPLETED="completed",e.SKIPPED="skipped"}(BreakState||(BreakState={}));export class BreakScheduler{constructor(e,t=!1,a={enabled:!1,maxGapSeconds:0},i=systemClock,r={enabled:!1,minBreakDurationSeconds:0},s=null,n=d,l=null,h=o){this.breaks=new Map,this.timing=k(),this.checkInterval=null,this.onBreakStart=null,this.onBreakApproaching=null,this.onBreakWarning=null,this.onBreakAborted=null,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.player=e,this.debug=t,this.chainingEnabled=a.enabled,this.maxChainGapMs=1e3*Math.max(0,a.maxGapSeconds),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,h);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 g({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 a=this.breaks.get(e.id);if(!a||a.state!==BreakState.PENDING)break;a.state=BreakState.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,[BreakState.PENDING,BreakState.CHAINED]):null}activateChainedBreak(e){const t=this.breaks.get(e);t&&(t.state=BreakState.ACTIVE)}findSuccessor(e,t=[BreakState.PENDING]){const a=this.getBreakStartTime(e);if(null===a)return null;const i=a+1e3*(e.duration??0);let r=null;for(const s of this.breaks.values()){if(s.break.id===e.id)continue;if(!t.includes(s.state))continue;const n=this.getBreakStartTime(s.break);if(null===n)continue;if(n<=a)continue;const d=n-i;d>this.maxChainGapMs||(d<-this.TIMING_TOLERANCE_MS||(!r||n<r.start)&&(r={break:s.break,start:n}))}return r?.break??null}updateManifest(e){this.timing.timebase=e.timebase,this.rewind.setAdStartDelayMs(1e3*Math.max(0,e.adStartDelay??0));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===BreakState.PENDING&&(e.break=t):(this.breaks.set(t.id,{break:t,state:BreakState.PENDING}),this.log(`📅 Break scheduled: "${t.id}" at ${this.describeStart(t)}`))}const t=new Set(e.breaks.map(e=>e.id));for(const[e,a]of this.breaks)t.has(e)||a.state!==BreakState.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?.(a.break));this.cleanupBreaks(t)}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.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)}describeStart(e){const t=r(e);if(null!==t)return t>0?`playback start + ${t}s`:"playback start";const a=i(e);return null!==a?a>0?`playback end + ${a}s`:"playback end":"string"==typeof e.start?new Date(e.start).toLocaleTimeString():String(e.start)}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()))}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){this.onBreakStart=e,this.onBreakApproaching=t??null,this.onBreakWarning=a??null,this.onBreakAborted=i??null,this.checkInterval&&clearInterval(this.checkInterval),this.checkInterval=setInterval(()=>{this.tick()},250),this.log("Scheduler started")}tick(){this.checkBreaks()}stop(){this.checkInterval&&(clearInterval(this.checkInterval),this.checkInterval=null),this.onBreakStart=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.preloadedBreakIds.delete(e))}setContentReloading(e){this.rewind.setContentReloading(e)}notifyResumeSeek(e,t){this.rewind.notifyResumeSeek(e,t)}notifyViewerSeek(){this.rewind.notifyViewerSeek()}cleanupBreaks(e){const t=[];for(const[a,i]of this.breaks)i.state!==BreakState.COMPLETED&&i.state!==BreakState.SKIPPED&&(e.has(a)||i.state===BreakState.ACTIVE)||t.push(a);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===BreakState.ACTIVE)return e.break;return null}getSecondsUntilBreak(e){const t=this.breaks.get(e);if(!t||t.state!==BreakState.PENDING)return null;const a=this.getCurrentTime();if(null===a)return null;const i=this.getBreakStartTime(t.break);return null===i?null:b(i,a)}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===BreakState.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===BreakState.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(){const e=this.getCurrentTime();this.rewind.trackPlayedTime(e);const a=this.rewind.isAdStartDelayActive();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===BreakState.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!==BreakState.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 o=this.getBreakStartTime(s.break);if(null===o)continue;const l=e-o,h=Math.round(-l/1e3);if(this.checkWarnings(s.break,h),h>0&&h<=15&&h%2==0&&this.log(`⏱️ Break "${s.break.id}" starts in ${h}s`),h>0&&h<=this.PRELOAD_AHEAD_SECONDS&&this.maybePreload(s.break,`${h}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)`),s.state=BreakState.SKIPPED,null===this.cueTracker&&this.retiredBreaks.set(s.break.id,s.break);break}if(null!==this.minSeenTime&&o<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)`),s.state=BreakState.SKIPPED,null===this.cueTracker&&this.retiredBreaks.set(s.break.id,s.break);break}this.log(`🎬 BREAK TRIGGERED: "${s.break.id}"`),s.state=BreakState.ACTIVE,this.onBreakStart&&this.onBreakStart(s.break,{tuneIn:!1,elapsedSec:e/1e3,remainingSec:t/1e3});break}if(l>this.TIMING_TOLERANCE_MS){const t=o+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=BreakState.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)`),s.state=BreakState.SKIPPED,null===this.cueTracker&&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(e.state=BreakState.SKIPPED);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=BreakState.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=BreakState.ACTIVE,this.onBreakStart&&this.onBreakStart(e.break,{tuneIn:!1,elapsedSec:0,remainingSec:e.break.duration??0}))}isAdStartDelayActive(){return this.rewind.isAdStartDelayActive()}getCurrentTime(){return u(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 s(e.start)?null:c(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.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.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&&h("[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 o}from"../types/OptiViewAdsConfig";import{PTS_MATCH_TOLERANCE_SECONDS as l}from"./MetadataCueTracker";import{debugLog as h}from"../diagnostics/debugLog";import{createBreakTimingState as k,getBreakStartTime as c,getCurrentTime as u,secondsUntilBreak as b}from"./breakTiming";import{RewindRearm as g}from"./RewindRearm";export const systemClock={now:()=>Date.now()};export var BreakState;!function(e){e.PENDING="pending",e.CHAINED="chained",e.ACTIVE="active",e.COMPLETED="completed",e.SKIPPED="skipped"}(BreakState||(BreakState={}));export class BreakScheduler{constructor(e,t=!1,a={enabled:!1,maxGapSeconds:0},i=systemClock,r={enabled:!1,minBreakDurationSeconds:0},s=null,n=d,l=null,h=o){this.breaks=new Map,this.timing=k(),this.checkInterval=null,this.onBreakStart=null,this.onBreakApproaching=null,this.onBreakWarning=null,this.onBreakAborted=null,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=a.enabled,this.maxChainGapMs=1e3*Math.max(0,a.maxGapSeconds),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,h);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 g({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 a=this.breaks.get(e.id);if(!a||a.state!==BreakState.PENDING)break;a.state=BreakState.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,[BreakState.PENDING,BreakState.CHAINED]):null}activateChainedBreak(e){const t=this.breaks.get(e);t&&(t.state=BreakState.ACTIVE)}findSuccessor(e,t=[BreakState.PENDING]){const a=this.getBreakStartTime(e);if(null===a)return null;const i=a+1e3*(e.duration??0);let r=null;for(const s of this.breaks.values()){if(s.break.id===e.id)continue;if(!t.includes(s.state))continue;const n=this.getBreakStartTime(s.break);if(null===n)continue;if(n<=a)continue;const d=n-i;d>this.maxChainGapMs||(d<-this.TIMING_TOLERANCE_MS||(!r||n<r.start)&&(r={break:s.break,start:n}))}return r?.break??null}updateManifest(e){this.timing.timebase=e.timebase,this.rewind.setAdStartDelayMs(1e3*Math.max(0,e.adStartDelay??0));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===BreakState.PENDING&&(e.break=t):(this.breaks.set(t.id,{break:t,state:BreakState.PENDING}),this.log(`📅 Break scheduled: "${t.id}" at ${this.describeStart(t)}`))}const t=new Set(e.breaks.map(e=>e.id));for(const[e,a]of this.breaks)t.has(e)||a.state!==BreakState.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?.(a.break));this.cleanupBreaks(t)}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.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)}describeStart(e){const t=r(e);if(null!==t)return t>0?`playback start + ${t}s`:"playback start";const a=i(e);return null!==a?a>0?`playback end + ${a}s`:"playback end":"string"==typeof e.start?new Date(e.start).toLocaleTimeString():String(e.start)}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()))}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){this.onBreakStart=e,this.onBreakApproaching=t??null,this.onBreakWarning=a??null,this.onBreakAborted=i??null,this.checkInterval&&clearInterval(this.checkInterval),this.checkInterval=setInterval(()=>{this.tick()},250),this.log("Scheduler started")}tick(){this.checkBreaks()}stop(){this.checkInterval&&(clearInterval(this.checkInterval),this.checkInterval=null),this.onBreakStart=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.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()}cleanupBreaks(e){const t=[];for(const[a,i]of this.breaks)i.state!==BreakState.COMPLETED&&i.state!==BreakState.SKIPPED&&(e.has(a)||i.state===BreakState.ACTIVE)||t.push(a);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===BreakState.ACTIVE)return e.break;return null}getSecondsUntilBreak(e){const t=this.breaks.get(e);if(!t||t.state!==BreakState.PENDING)return null;const a=this.getCurrentTime();if(null===a)return null;const i=this.getBreakStartTime(t.break);return null===i?null:b(i,a)}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===BreakState.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===BreakState.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.startupSeeking)return;const e=this.getCurrentTime();this.rewind.trackPlayedTime(e);const a=this.rewind.isAdStartDelayActive();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===BreakState.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!==BreakState.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 o=this.getBreakStartTime(s.break);if(null===o)continue;const l=e-o,h=Math.round(-l/1e3);if(this.checkWarnings(s.break,h),h>0&&h<=15&&h%2==0&&this.log(`⏱️ Break "${s.break.id}" starts in ${h}s`),h>0&&h<=this.PRELOAD_AHEAD_SECONDS&&this.maybePreload(s.break,`${h}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)`),s.state=BreakState.SKIPPED,null===this.cueTracker&&this.retiredBreaks.set(s.break.id,s.break);break}if(null!==this.minSeenTime&&o<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)`),s.state=BreakState.SKIPPED,null===this.cueTracker&&this.retiredBreaks.set(s.break.id,s.break);break}this.log(`🎬 BREAK TRIGGERED: "${s.break.id}"`),s.state=BreakState.ACTIVE,this.onBreakStart&&this.onBreakStart(s.break,{tuneIn:!1,elapsedSec:e/1e3,remainingSec:t/1e3});break}if(l>this.TIMING_TOLERANCE_MS){const t=o+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=BreakState.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)`),s.state=BreakState.SKIPPED,null===this.cueTracker&&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(e.state=BreakState.SKIPPED);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=BreakState.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=BreakState.ACTIVE,this.onBreakStart&&this.onBreakStart(e.break,{tuneIn:!1,elapsedSec:0,remainingSec:e.break.duration??0}))}isAdStartDelayActive(){return this.rewind.isAdStartDelayActive()}getCurrentTime(){return u(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 s(e.start)?null:c(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.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.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&&h("[OptiViewAds:BreakScheduler]",...e)}}
@@ -18,6 +18,7 @@ export declare class CmcdAdReporter {
18
18
  private sessionKeys;
19
19
  private activeBreakId;
20
20
  private breakBeginTimestamp;
21
+ private breakAvailableMs;
21
22
  private breakRequestedMs;
22
23
  private firstAdReported;
23
24
  /** Playable (non-companion) ad milliseconds delivered so far in the break. */
@@ -40,6 +41,9 @@ export declare class CmcdAdReporter {
40
41
  private pendingAdStart;
41
42
  private activeAdWatchedSec;
42
43
  private activeAdDeclaredSec;
44
+ private activeAdIsVast;
45
+ /** The VAST creative duration the ad player resolved, from `adtimeupdate`. */
46
+ private activeAdResolvedSec;
43
47
  private skipRequestedBreakId;
44
48
  private abortedBreakId;
45
49
  private cutBreakId;
@@ -84,6 +88,12 @@ export declare class CmcdAdReporter {
84
88
  private onClick;
85
89
  private onAdError;
86
90
  private emitCustomWithAdContext;
91
+ /**
92
+ * The seconds the active ad fills: the VAST creative duration the ad player
93
+ * resolved, within the manifest-declared slot; the declared duration when
94
+ * the player resolved none.
95
+ */
96
+ private activeAdFilledSec;
87
97
  /**
88
98
  * The ad keys for a milestone/click/error row: the full `as` key set when the
89
99
  * event's asset is the active ad (same match rule as `adtimeupdate`),
@@ -1 +1 @@
1
- import{LensEventType as e,LensToken as t}from"../vendor/lens-sdk/index.js";import{AssetType as i,BreakFormat as s}from"../types/BreakManifest";import{VendorAdError as a}from"../types/VendorAdError";import{SDK_VERSION as d}from"../generated/version";const r={[s.SINGLE]:"s",[s.DOUBLE]:"d",[s.LSHAPE_AD]:"la",[s.LSHAPE_CONTENT]:"lc",[s.OVERLAY]:"o"},n={[i.STATIC]:"s",[i.VAST]:"va",[i.VENDOR]:"ve"};function h(e){return n[e]??e}export class CmcdAdReporter{constructor(e,t){this.getHandle=e,this.log=t,this.sessionKeys={},this.activeBreakId=null,this.breakBeginTimestamp=null,this.breakRequestedMs=null,this.firstAdReported=!1,this.breakFilledMs=0,this.breakWatchedMs=0,this.activeAdKeys=null,this.activeAdCompanion=null,this.activeAdAssetId=null,this.activeAdIsVendor=!1,this.activeAdBaseSec=0,this.breakLastTimeSec=0,this.pendingAdStart=null,this.activeAdWatchedSec=0,this.activeAdDeclaredSec=null,this.skipRequestedBreakId=null,this.abortedBreakId=null,this.cutBreakId=null,this.erroredAssetId=null,this.finalizedBreakId=null}onSessionStart(e){this.sessionKeys={adv:d,admv:e.version,...e.organizationId?{adoid:e.organizationId}:{},...e.channelId?{adcid:e.channelId}:{},...e.eventId?{adeid:e.eventId}:{}},this.updateHeartbeat()}onSessionEnd(){this.finalizeActiveBreak(),this.sessionKeys={},this.resetBreakState(),this.guard("setHeartbeatPayload",e=>e.setHeartbeatPayload())}finalizeActiveBreak(){if(null===this.activeBreakId)return;const e=this.activeBreakId;this.emitPendingAdStart(null),null!==this.activeAdKeys&&this.emitAdEnd(this.activeAdKeys,this.activeAdCompanion,"a"),this.emitBreakEnd(e),this.finalizedBreakId=e}onSkipRequested(e){this.skipRequestedBreakId=e}onBreakAborted(e){this.abortedBreakId=e}onBreakCut(e){this.cutBreakId=e}onEvent(e){switch(e.type){case"adbreakbegin":this.onBreakBegin(e);break;case"adbegin":this.onAdBegin(e);break;case"adtimeupdate":{const t=e;this.breakLastTimeSec=Math.max(this.breakLastTimeSec,t.currentTime);null!==this.pendingAdStart&&(t.asset.id===this.pendingAdStart.assetId||this.pendingAdStart.isVendor&&t.break.id===this.activeBreakId)&&t.currentTime>(this.pendingAdStart?.isVendor?this.pendingAdStart.baseSec:0)&&null!==this.breakBeginTimestamp&&this.emitPendingAdStart(t.timestamp-this.breakBeginTimestamp);if(t.asset.id===this.activeAdAssetId||this.activeAdIsVendor&&t.break.id===this.activeBreakId){const e=t.currentTime-this.activeAdBaseSec;this.activeAdWatchedSec=Math.max(this.activeAdWatchedSec,e)}break}case"adend":this.onAdEnd(e);break;case"adbreakend":this.onBreakEnd(e);break;case"adfirstquartile":case"admidpoint":case"adthirdquartile":this.onMilestone(e);break;case"adclick":this.onClick(e);break;case"aderror":this.onAdError(e)}}onBreakBegin(i){this.resetBreakState(),this.finalizedBreakId=null,this.activeBreakId=i.break.id,this.breakBeginTimestamp=i.timestamp;const s=Math.round(1e3*i.remainingSec),a=this.requestedPlayableMs(i.break,i.format,s);this.breakRequestedMs=a,this.guard("abs",d=>{return d.emit(e.AD_BREAK_START,{custom:{...this.sessionKeys,brid:i.break.id,brst:String(i.break.start),brmd:Math.round(1e3*i.break.duration),brad:s,...null!==a?{brrd:a}:{},...i.format?{brf:new t((n=i.format,r[n]??n))}:{}}});var n}),this.updateHeartbeat()}onAdBegin(e){this.emitPendingAdStart(null);const s=e.asset,a=s.interaction?.clickThrough,d=!this.firstAdReported&&null!==this.breakBeginTimestamp;this.firstAdReported=!0;const r={brid:e.break.id,adasid:s.id,adid:e.adId??s.id,...void 0!==e.podPosition?{adpos:String(e.podPosition)}:{},...void 0!==s.duration?{adasd:Math.round(1e3*s.duration)}:{},adast:new t(h(s.type)),...a?{adcl:!0}:{},...e.creativeId?{adgcrid:e.creativeId}:{},...e.adId&&"string"==typeof s.uri&&"vendor"===s.type?{adgpid:s.uri}:{}};this.activeAdKeys=r,this.activeAdCompanion=s.companion?{id:s.companion.id,parentAssetId:s.id}:null,this.activeAdAssetId=s.id,this.activeAdIsVendor=s.type===i.VENDOR,this.activeAdBaseSec=this.activeAdIsVendor?this.breakLastTimeSec:0,this.activeAdWatchedSec=0,this.activeAdDeclaredSec=s.duration??null;const n={...this.sessionKeys,...r},o=s.companion?{...this.sessionKeys,brid:e.break.id,adasid:s.companion.id,adid:s.companion.id,adast:new t(h(s.companion.type)),adcomp:!0,adpasid:s.id}:null;d?this.pendingAdStart={assetId:s.id,custom:n,companionCustom:o,isVendor:this.activeAdIsVendor,baseSec:this.activeAdBaseSec}:this.emitAdStart(n,o),this.updateHeartbeat()}emitAdStart(t,i){this.guard("as",i=>i.emit(e.AD_START,{custom:t})),i&&this.guard("as",t=>t.emit(e.AD_START,{custom:i}))}emitPendingAdStart(e){const t=this.pendingAdStart;if(!t)return;this.pendingAdStart=null;const i=null!==e?{...t.custom,brttf:e}:t.custom;this.emitAdStart(i,t.companionCustom)}onAdEnd(e){if(e.break.id===this.finalizedBreakId)return;this.emitPendingAdStart(null);const t=e.asset,i=this.adEndReason(e),s=this.activeAdKeys??{brid:e.break.id,adasid:t.id,adid:e.adId??t.id},a=t.companion?{id:t.companion.id,parentAssetId:t.id}:null;this.emitAdEnd(s,a,i)}emitAdEnd(i,s,a){const d=null!==this.activeAdDeclaredSec?Math.round(1e3*this.activeAdDeclaredSec):null,r=Math.round(1e3*this.activeAdWatchedSec),n=null!==d?Math.min(r,d):r;"s"===a&&this.guard("sk",t=>t.emit(e.AD_SKIP,{custom:{...this.sessionKeys,...i}})),this.guard("ae",s=>s.emit(e.AD_END,{custom:{...this.sessionKeys,...i,ader:new t(a),adwd:n}})),s&&this.guard("ae",d=>d.emit(e.AD_END,{custom:{...this.sessionKeys,brid:i.brid,adasid:s.id,adid:s.id,adcomp:!0,adpasid:s.parentAssetId,ader:new t(a)}})),this.breakWatchedMs+=n,this.breakFilledMs+=null!==d?d:n,this.activeAdKeys=null,this.activeAdCompanion=null,this.activeAdAssetId=null,this.activeAdIsVendor=!1,this.activeAdBaseSec=0,this.activeAdWatchedSec=0,this.activeAdDeclaredSec=null,this.erroredAssetId===i.adasid&&(this.erroredAssetId=null),this.updateHeartbeat()}onBreakEnd(e){e.break.id!==this.finalizedBreakId&&(this.emitPendingAdStart(null),this.emitBreakEnd(e.break.id))}emitBreakEnd(t){const i=null!==this.breakRequestedMs?Math.min(this.breakFilledMs,this.breakRequestedMs):this.breakFilledMs,s=Math.min(this.breakWatchedMs,i);this.guard("abe",a=>a.emit(e.AD_BREAK_END,{custom:{...this.sessionKeys,brid:t,brfd:i,brwd:s}})),this.resetBreakState(),this.updateHeartbeat()}onMilestone(e){this.emitPendingAdStart(null),this.emitCustomWithAdContext(e.type,e.break,e.asset)}onClick(e){this.emitPendingAdStart(null),this.emitCustomWithAdContext("adclick",e.break,e.asset)}onAdError(e){"ad"===e.source&&(this.emitPendingAdStart(null),e.asset&&(this.erroredAssetId=e.asset.id),this.guard("aderror",t=>t.emitCustom("aderror",{custom:{...this.sessionKeys,adec:e.asset?"DA-AD-PLAYBACK-ERROR":"DA-BREAK-NO-PLAYABLE-ASSET",...e.error instanceof a?{adevc:e.error.vendorCode}:{},...e.break&&e.asset?this.adContextKeys(e.break,e.asset):{...e.break?{brid:e.break.id}:{},...e.asset?{adasid:e.asset.id,adid:e.asset.id}:{}}}})))}emitCustomWithAdContext(e,t,i){this.guard(e,s=>s.emitCustom(e,{custom:{...this.sessionKeys,...this.adContextKeys(t,i)}}))}adContextKeys(e,t){return(t.id===this.activeAdAssetId||this.activeAdIsVendor&&e.id===this.activeBreakId)&&null!==this.activeAdKeys?{...this.activeAdKeys}:{brid:e.id,adasid:t.id,...void 0!==this.activeAdKeys?.adid?{adid:this.activeAdKeys.adid}:{}}}adEndReason(e){return this.skipRequestedBreakId===e.break.id?(this.skipRequestedBreakId=null,"s"):this.erroredAssetId===e.asset.id?"e":this.abortedBreakId===e.break.id?"a":this.cutBreakId===e.break.id?(this.cutBreakId=null,"a"):"c"}requestedPlayableMs(e,t,i){const s=Array.isArray(e.variant)?e.variant:[e.variant],a=s.find(e=>e.format===t)??s[0];if(!a)return null;if(0===a.assets.length)return null;const d=a.assets.map(e=>e.duration);if(d.some(e=>void 0===e||!Number.isFinite(e)||e<=0))return i;const r=d.reduce((e,t)=>e+(t??0),0);return Math.min(Math.round(1e3*r),i)}updateHeartbeat(){this.guard("setHeartbeatPayload",e=>e.setHeartbeatPayload({custom:{...this.sessionKeys,...this.activeBreakId?{brid:this.activeBreakId}:{},...void 0!==this.activeAdKeys?.adid?{adid:this.activeAdKeys.adid}:{}}}))}resetBreakState(){this.emitPendingAdStart(null),this.activeBreakId=null,this.breakBeginTimestamp=null,this.breakRequestedMs=null,this.firstAdReported=!1,this.breakFilledMs=0,this.breakWatchedMs=0,this.activeAdKeys=null,this.activeAdCompanion=null,this.activeAdAssetId=null,this.activeAdIsVendor=!1,this.activeAdBaseSec=0,this.breakLastTimeSec=0,this.activeAdWatchedSec=0,this.activeAdDeclaredSec=null,this.skipRequestedBreakId=null,this.abortedBreakId=null,this.cutBreakId=null,this.erroredAssetId=null}guard(e,t){const i=this.getHandle();if(i)try{t(i)}catch(t){this.log(`Lens CMCD ${e} failed:`,t)}}}
1
+ import{LensEventType as e,LensToken as t}from"../vendor/lens-sdk/index.js";import{AssetType as i,BreakFormat as s,getBreakEvent as a}from"../types/BreakManifest";import{adErrorCode as d}from"../types/AdError";import{VendorAdError as r}from"../types/VendorAdError";import{SDK_VERSION as n}from"../generated/version";const h={[s.SINGLE]:"s",[s.DOUBLE]:"d",[s.LSHAPE_AD]:"la",[s.LSHAPE_CONTENT]:"lc",[s.OVERLAY]:"o"},o={[i.STATIC]:"s",[i.VAST]:"va",[i.VENDOR]:"ve"};function l(e){const t=a(e);if(null!==t)return t;const{start:i}=e;return"number"==typeof i?String(Math.round(1e3*i)):"string"==typeof i?i:i.event}function c(e){return o[e]??e}export class CmcdAdReporter{constructor(e,t){this.getHandle=e,this.log=t,this.sessionKeys={},this.activeBreakId=null,this.breakBeginTimestamp=null,this.breakAvailableMs=null,this.breakRequestedMs=null,this.firstAdReported=!1,this.breakFilledMs=0,this.breakWatchedMs=0,this.activeAdKeys=null,this.activeAdCompanion=null,this.activeAdAssetId=null,this.activeAdIsVendor=!1,this.activeAdBaseSec=0,this.breakLastTimeSec=0,this.pendingAdStart=null,this.activeAdWatchedSec=0,this.activeAdDeclaredSec=null,this.activeAdIsVast=!1,this.activeAdResolvedSec=null,this.skipRequestedBreakId=null,this.abortedBreakId=null,this.cutBreakId=null,this.erroredAssetId=null,this.finalizedBreakId=null}onSessionStart(e){this.sessionKeys={adv:n,admv:e.version,...e.organizationId?{adoid:e.organizationId}:{},...e.channelId?{adcid:e.channelId}:{},...e.eventId?{adeid:e.eventId}:{}},this.updateHeartbeat()}onSessionEnd(){this.finalizeActiveBreak(),this.sessionKeys={},this.resetBreakState(),this.guard("setHeartbeatPayload",e=>e.setHeartbeatPayload())}finalizeActiveBreak(){if(null===this.activeBreakId)return;const e=this.activeBreakId;this.emitPendingAdStart(null),null!==this.activeAdKeys&&this.emitAdEnd(this.activeAdKeys,this.activeAdCompanion,"a"),this.emitBreakEnd(e),this.finalizedBreakId=e}onSkipRequested(e){this.skipRequestedBreakId=e}onBreakAborted(e){this.abortedBreakId=e}onBreakCut(e){this.cutBreakId=e}onEvent(e){switch(e.type){case"adbreakbegin":this.onBreakBegin(e);break;case"adbegin":this.onAdBegin(e);break;case"adtimeupdate":{const t=e;this.breakLastTimeSec=Math.max(this.breakLastTimeSec,t.currentTime);null!==this.pendingAdStart&&(t.asset.id===this.pendingAdStart.assetId||this.pendingAdStart.isVendor&&t.break.id===this.activeBreakId)&&t.currentTime>(this.pendingAdStart?.isVendor?this.pendingAdStart.baseSec:0)&&null!==this.breakBeginTimestamp&&this.emitPendingAdStart(t.timestamp-this.breakBeginTimestamp);if(t.asset.id===this.activeAdAssetId||this.activeAdIsVendor&&t.break.id===this.activeBreakId){const e=t.currentTime-this.activeAdBaseSec;this.activeAdWatchedSec=Math.max(this.activeAdWatchedSec,e),this.activeAdIsVast&&Number.isFinite(t.duration)&&t.duration>0&&(this.activeAdResolvedSec=t.duration)}break}case"adend":this.onAdEnd(e);break;case"adbreakend":this.onBreakEnd(e);break;case"adfirstquartile":case"admidpoint":case"adthirdquartile":this.onMilestone(e);break;case"adclick":this.onClick(e);break;case"aderror":this.onAdError(e)}}onBreakBegin(i){this.resetBreakState(),this.finalizedBreakId=null,this.activeBreakId=i.break.id,this.breakBeginTimestamp=i.timestamp;const s=Math.round(1e3*i.remainingSec),a=this.requestedPlayableMs(i.break,i.format,s)??s;this.breakAvailableMs=s,this.breakRequestedMs=a,this.guard("abs",d=>{return d.emit(e.AD_BREAK_START,{custom:{...this.sessionKeys,brid:i.break.id,brst:l(i.break),brmd:Math.round(1e3*i.break.duration),brad:s,brrd:a,...i.format?{brf:new t((r=i.format,h[r]??r))}:{}}});var r}),this.updateHeartbeat()}onAdBegin(e){this.emitPendingAdStart(null);const s=e.asset,a=s.interaction?.clickThrough,d=!this.firstAdReported&&null!==this.breakBeginTimestamp;this.firstAdReported=!0;const r={brid:e.break.id,adasid:s.id,adid:e.adId??s.id,...void 0!==e.podPosition?{adpos:String(e.podPosition)}:{},...void 0!==s.duration?{adasd:Math.round(1e3*s.duration)}:{},adast:new t(c(s.type)),...a?{adcl:!0}:{},...e.creativeId?{adgcrid:e.creativeId}:{},...e.adId&&"string"==typeof s.uri&&"vendor"===s.type?{adgpid:s.uri}:{}};this.activeAdKeys=r,this.activeAdCompanion=s.companion?{id:s.companion.id,parentAssetId:s.id}:null,this.activeAdAssetId=s.id,this.activeAdIsVendor=s.type===i.VENDOR,this.activeAdBaseSec=this.activeAdIsVendor?this.breakLastTimeSec:0,this.activeAdWatchedSec=0,this.activeAdDeclaredSec=s.duration??null,this.activeAdIsVast=s.type===i.VAST,this.activeAdResolvedSec=null;const n={...this.sessionKeys,...r},h=s.companion?{...this.sessionKeys,brid:e.break.id,adasid:s.companion.id,adid:s.companion.id,adast:new t(c(s.companion.type)),adcomp:!0,adpasid:s.id}:null;d?this.pendingAdStart={assetId:s.id,custom:n,companionCustom:h,isVendor:this.activeAdIsVendor,baseSec:this.activeAdBaseSec}:this.emitAdStart(n,h),this.updateHeartbeat()}emitAdStart(t,i){this.guard("as",i=>i.emit(e.AD_START,{custom:t})),i&&this.guard("as",t=>t.emit(e.AD_START,{custom:i}))}emitPendingAdStart(e){const t=this.pendingAdStart;if(!t)return;this.pendingAdStart=null;const i=null!==e?{...t.custom,brttf:e}:t.custom;this.emitAdStart(i,t.companionCustom)}onAdEnd(e){if(e.break.id===this.finalizedBreakId)return;this.emitPendingAdStart(null);const t=e.asset,i=this.adEndReason(e),s=this.activeAdKeys??{brid:e.break.id,adasid:t.id,adid:e.adId??t.id},a=t.companion?{id:t.companion.id,parentAssetId:t.id}:null;this.emitAdEnd(s,a,i)}emitAdEnd(i,s,a){const d=this.activeAdFilledSec(),r=null!==d?Math.round(1e3*d):null,n=Math.round(1e3*this.activeAdWatchedSec),h=null!==r?Math.min(n,r):n;"s"===a&&this.guard("sk",t=>t.emit(e.AD_SKIP,{custom:{...this.sessionKeys,...i}})),this.guard("ae",s=>s.emit(e.AD_END,{custom:{...this.sessionKeys,...i,ader:new t(a),adwd:h}})),s&&this.guard("ae",d=>d.emit(e.AD_END,{custom:{...this.sessionKeys,brid:i.brid,adasid:s.id,adid:s.id,adcomp:!0,adpasid:s.parentAssetId,ader:new t(a)}})),this.breakWatchedMs+=h,this.breakFilledMs+=null!==r?r:h,this.activeAdKeys=null,this.activeAdCompanion=null,this.activeAdAssetId=null,this.activeAdIsVendor=!1,this.activeAdBaseSec=0,this.activeAdWatchedSec=0,this.activeAdDeclaredSec=null,this.activeAdIsVast=!1,this.activeAdResolvedSec=null,this.erroredAssetId===i.adasid&&(this.erroredAssetId=null),this.updateHeartbeat()}onBreakEnd(e){if(e.break.id!==this.finalizedBreakId){if(this.emitPendingAdStart(null),null!==this.breakAvailableMs){const t=this.requestedPlayableMs(e.break,e.format,this.breakAvailableMs);null!==t&&(this.breakRequestedMs=t)}this.emitBreakEnd(e.break.id)}}emitBreakEnd(t){const i=this.breakAvailableMs,s=null!==this.breakRequestedMs&&null!==i?Math.min(this.breakRequestedMs,i):this.breakRequestedMs,a=null!==s?Math.min(this.breakFilledMs,s):this.breakFilledMs,d=Math.min(this.breakWatchedMs,a);this.guard("abe",r=>r.emit(e.AD_BREAK_END,{custom:{...this.sessionKeys,brid:t,...null!==i?{brad:i}:{},...null!==s?{brrd:s}:{},brfd:a,brwd:d}})),this.resetBreakState(),this.updateHeartbeat()}onMilestone(e){this.emitPendingAdStart(null),this.emitCustomWithAdContext(e.type,e.break,e.asset)}onClick(e){this.emitPendingAdStart(null),this.emitCustomWithAdContext("adclick",e.break,e.asset)}onAdError(e){"ad"===e.source&&(this.emitPendingAdStart(null),e.asset&&(this.erroredAssetId=e.asset.id),this.guard("aderror",t=>t.emitCustom("aderror",{custom:{...this.sessionKeys,adec:e.asset?d(e.error):"DA-BREAK-NO-PLAYABLE-ASSET",...e.error instanceof r?{adevc:e.error.vendorCode}:{},...e.break&&e.asset?this.adContextKeys(e.break,e.asset):{...e.break?{brid:e.break.id}:{},...e.asset?{adasid:e.asset.id,adid:e.asset.id}:{}}}})))}emitCustomWithAdContext(e,t,i){this.guard(e,s=>s.emitCustom(e,{custom:{...this.sessionKeys,...this.adContextKeys(t,i)}}))}activeAdFilledSec(){const e=this.activeAdDeclaredSec,t=this.activeAdResolvedSec;return null===t?e:null===e?t:Math.min(t,e)}adContextKeys(e,t){return(t.id===this.activeAdAssetId||this.activeAdIsVendor&&e.id===this.activeBreakId)&&null!==this.activeAdKeys?{...this.activeAdKeys}:{brid:e.id,adasid:t.id,...void 0!==this.activeAdKeys?.adid?{adid:this.activeAdKeys.adid}:{}}}adEndReason(e){return this.skipRequestedBreakId===e.break.id?(this.skipRequestedBreakId=null,"s"):this.erroredAssetId===e.asset.id?"e":this.abortedBreakId===e.break.id?"a":this.cutBreakId===e.break.id?(this.cutBreakId=null,"a"):"c"}requestedPlayableMs(e,t,i){const s=Array.isArray(e.variant)?e.variant:[e.variant],a=s.find(e=>e.format===t)??s[0];if(!a)return null;if(0===a.assets.length)return null;const d=a.assets.map(e=>e.duration);if(d.some(e=>void 0===e||!Number.isFinite(e)||e<=0))return i;const r=d.reduce((e,t)=>e+(t??0),0);return Math.min(Math.round(1e3*r),i)}updateHeartbeat(){this.guard("setHeartbeatPayload",e=>e.setHeartbeatPayload({custom:{...this.sessionKeys,...this.activeBreakId?{brid:this.activeBreakId}:{},...void 0!==this.activeAdKeys?.adid?{adid:this.activeAdKeys.adid}:{}}}))}resetBreakState(){this.emitPendingAdStart(null),this.activeBreakId=null,this.breakBeginTimestamp=null,this.breakAvailableMs=null,this.breakRequestedMs=null,this.firstAdReported=!1,this.breakFilledMs=0,this.breakWatchedMs=0,this.activeAdKeys=null,this.activeAdCompanion=null,this.activeAdAssetId=null,this.activeAdIsVendor=!1,this.activeAdBaseSec=0,this.breakLastTimeSec=0,this.activeAdWatchedSec=0,this.activeAdDeclaredSec=null,this.activeAdIsVast=!1,this.activeAdResolvedSec=null,this.skipRequestedBreakId=null,this.abortedBreakId=null,this.cutBreakId=null,this.erroredAssetId=null}guard(e,t){const i=this.getHandle();if(i)try{t(i)}catch(t){this.log(`Lens CMCD ${e} failed:`,t)}}}
@@ -5,7 +5,8 @@ import { Asset } from '../types/BreakManifest';
5
5
  * reports the real composition per ad at runtime via `StreamEvent.getAd().getAdPodInfo()`.
6
6
  * Contract: every `adbegin` gets exactly one `adend`; a new ad closes the previous one even if
7
7
  * IMA never reported completion; {@link finish} closes whatever is open; re-reporting the open
8
- * ad is idempotent (IMA re-fires `STARTED` when a viewer seeks back into an ad).
8
+ * ad is idempotent (IMA re-fires `STARTED` when a viewer seeks back into an ad). IMA can split
9
+ * one pod into several ad breaks, so {@link onAdBreakEnded} only finishes once the last ad was seen.
9
10
  */
10
11
  /** One IMA pod-ad report, normalized by the render layer so this module never sees an IMA object. */
11
12
  export interface GamPodAdInfo {
@@ -51,15 +52,25 @@ export declare class GamPodAdTracker {
51
52
  private openPosition;
52
53
  private reportedAnyAd;
53
54
  private finished;
55
+ private lastPosition;
56
+ private lastTotal;
54
57
  constructor(podAsset: Asset);
55
58
  /** The ad currently open, or null. Drives quartile attribution. */
56
59
  get currentAd(): GamPodCurrentAd | null;
57
60
  /** True once IMA reported at least one ad; while false the pod is still reported the legacy way. */
58
61
  get sawAnyAd(): boolean;
62
+ /** 1-based position of the last ad IMA reported; 0 before any. */
63
+ get lastAdPosition(): number;
64
+ /** Ads in the pod per the last IMA report; 0 when IMA did not say. */
65
+ get lastTotalAds(): number;
66
+ /** True once IMA reported the pod's last ad, so anything after it is slate. */
67
+ get reportedLastAd(): boolean;
59
68
  /** Closes the previous ad if it never completed, then opens the reported one. Idempotent for the open ad. */
60
69
  onAdStarted(info: GamPodAdInfo): GamPodAdStep[];
61
70
  /** IMA reported the open ad completing. No-op when nothing is open. */
62
71
  onAdComplete(): GamPodAdStep[];
72
+ /** IMA ended an ad break while pod stream remains: close the open ad, and finish only after the last ad. */
73
+ onAdBreakEnded(): GamPodAdStep[];
63
74
  /** Close whatever is open and refuse further reports, so a late IMA event cannot reopen a finished pod. */
64
75
  finish(): GamPodAdStep[];
65
76
  /** Emit the `adend` for the open ad, if any, and clear it. */
@@ -1 +1 @@
1
- export function derivePodAdAsset(t,e,i){return{...t,id:`${t.id}-ad-${e}`,..."number"==typeof i&&Number.isFinite(i)&&i>0?{duration:i}:{}}}export class GamPodAdTracker{constructor(t){this.podAsset=t,this.open=null,this.openPosition=0,this.reportedAnyAd=!1,this.finished=!1}get currentAd(){return this.open}get sawAnyAd(){return this.reportedAnyAd}onAdStarted(t){if(this.finished)return[];const e=function(t){if("number"!=typeof t||!Number.isFinite(t))return;const e=Math.floor(t);return e>=1?e:void 0}(t.adPosition),i=e??1;if(this.open&&this.openPosition===i)return[];const n=this.closeOpen(),s=i-1;var o,r;return this.open={asset:derivePodAdAsset(this.podAsset,i,t.duration),adIndex:s,totalAds:(o=t.totalAds,r=i,"number"==typeof o&&Number.isFinite(o)?Math.max(r,Math.floor(o)):r),adPosition:e,adId:t.adId,creativeId:t.creativeId},this.openPosition=i,this.reportedAnyAd=!0,n.push({type:"adbegin",...this.open}),n}onAdComplete(){return this.finished?[]:this.closeOpen()}finish(){if(this.finished)return[];const t=this.closeOpen();return this.finished=!0,t}closeOpen(){if(!this.open)return[];const t=[{type:"adend",...this.open}];return this.open=null,this.openPosition=0,t}}
1
+ export function derivePodAdAsset(t,i,s){return{...t,id:`${t.id}-ad-${i}`,..."number"==typeof s&&Number.isFinite(s)&&s>0?{duration:s}:{}}}function t(t,i){return"number"==typeof t&&Number.isFinite(t)?Math.max(i,Math.floor(t)):i}export class GamPodAdTracker{constructor(t){this.podAsset=t,this.open=null,this.openPosition=0,this.reportedAnyAd=!1,this.finished=!1,this.lastPosition=0,this.lastTotal=0}get currentAd(){return this.open}get sawAnyAd(){return this.reportedAnyAd}get lastAdPosition(){return this.lastPosition}get lastTotalAds(){return this.lastTotal}get reportedLastAd(){return this.lastTotal>0&&this.lastPosition>=this.lastTotal}onAdStarted(i){if(this.finished)return[];const s=function(t){if("number"!=typeof t||!Number.isFinite(t))return;const i=Math.floor(t);return i>=1?i:void 0}(i.adPosition),e=s??1;if(this.open&&this.openPosition===e)return[];const n=this.closeOpen(),o=e-1;return this.open={asset:derivePodAdAsset(this.podAsset,e,i.duration),adIndex:o,totalAds:t(i.totalAds,e),adPosition:s,adId:i.adId,creativeId:i.creativeId},this.openPosition=e,this.lastPosition=e,this.lastTotal=t(i.totalAds,0),this.reportedAnyAd=!0,n.push({type:"adbegin",...this.open}),n}onAdComplete(){return this.finished?[]:this.closeOpen()}onAdBreakEnded(){return this.finished?[]:this.reportedLastAd?this.finish():this.closeOpen()}finish(){if(this.finished)return[];const t=this.closeOpen();return this.finished=!0,t}closeOpen(){if(!this.open)return[];const t=[{type:"adend",...this.open}];return this.open=null,this.openPosition=0,t}}
@@ -1,9 +1,11 @@
1
- import type { LensHandle } from '../vendor/lens-sdk/index.js';
1
+ import type { LensBatch, LensHandle } from '../vendor/lens-sdk/index.js';
2
2
  /**
3
3
  * The Ads SDK's product identity in Lens: bound at join, spanning every session
4
4
  * of this SDK instance. Lens destination filters key on it.
5
5
  */
6
6
  export declare const LENS_NAMESPACE = "ads";
7
+ /** Batch built-in ingest reports without changing Lens's default heartbeat cadence. */
8
+ export declare const LENS_BATCH: LensBatch;
7
9
  /**
8
10
  * Ties Lens observability to the Ads SDK session lifecycle. Always on and not
9
11
  * integrator-configurable — no public config option reaches this class.
@@ -1 +1 @@
1
- import{Lens as s}from"../vendor/lens-sdk/index.js";import{configureLensBackend as e}from"./lensBackend";export const LENS_NAMESPACE="ads";export class LensReporter{constructor(s=()=>{}){this.log=s,this.lensHandle=null,this.joinFailed=!1,this.destroyed=!1}get handle(){return this.lensHandle}startSession(){const s=this.join();if(s)try{s.startSession()}catch(s){this.log("Lens session start failed:",s)}}endSession(){if(this.lensHandle)try{this.lensHandle.endSession()}catch(s){this.log("Lens session end failed:",s)}}destroy(){this.destroyed=!0;const s=this.lensHandle;if(this.lensHandle=null,s)try{s.dispose()}catch(s){this.log("Lens dispose failed:",s)}}join(){if(this.lensHandle)return this.lensHandle;if(this.destroyed||this.joinFailed)return null;try{e()}catch(s){this.log("Lens backend selection failed:",s)}try{this.lensHandle=s.join({namespace:"ads"})}catch(s){this.joinFailed=!0,this.log("Lens join failed:",s)}return this.lensHandle}}
1
+ import{Lens as s}from"../vendor/lens-sdk/index.js";import{configureLensBackend as e}from"./lensBackend";export const LENS_NAMESPACE="ads";export const LENS_BATCH={maxEvents:10,maxDelayMs:6e4};export class LensReporter{constructor(s=()=>{}){this.log=s,this.lensHandle=null,this.joinFailed=!1,this.destroyed=!1}get handle(){return this.lensHandle}startSession(){const s=this.join();if(s)try{s.startSession()}catch(s){this.log("Lens session start failed:",s)}}endSession(){if(this.lensHandle)try{this.lensHandle.endSession()}catch(s){this.log("Lens session end failed:",s)}}destroy(){this.destroyed=!0;const s=this.lensHandle;if(this.lensHandle=null,s)try{s.dispose()}catch(s){this.log("Lens dispose failed:",s)}}join(){if(this.lensHandle)return this.lensHandle;if(this.destroyed||this.joinFailed)return null;try{e()}catch(s){this.log("Lens backend selection failed:",s)}try{this.lensHandle=s.join({namespace:"ads",batch:LENS_BATCH})}catch(s){this.joinFailed=!0,this.log("Lens join failed:",s)}return this.lensHandle}}
@@ -0,0 +1,36 @@
1
+ import type { PlayerAdapter } from '../interfaces/PlayerAdapter';
2
+ import type { LiveStartPushbackConfig, PtsSource } from '../types/OptiViewAdsConfig';
3
+ /** Whether the startup adjustment applies, and which setting decided it. */
4
+ export interface LiveStartEnablement {
5
+ enabled: boolean;
6
+ enabledBy: 'config' | 'ptsSource';
7
+ }
8
+ /** An explicit `enabled` wins; otherwise the adjustment follows Anvato cue signaling. */
9
+ export declare function resolveLiveStartEnablement(config: LiveStartPushbackConfig | undefined, ptsSource: PtsSource | undefined): LiveStartEnablement;
10
+ interface LiveStartPushbackDeps {
11
+ blocked: () => boolean;
12
+ setSeeking: (active: boolean) => void;
13
+ diagnose: (code: string, message: string, context: Record<string, unknown>) => void;
14
+ }
15
+ export declare class LiveStartPushbackController {
16
+ private readonly player;
17
+ private readonly config;
18
+ private readonly deps;
19
+ private armed;
20
+ private playbackSeen;
21
+ private startOffsetRequested;
22
+ private generation;
23
+ private cleanupLanding;
24
+ private readonly enablement;
25
+ constructor(player: PlayerAdapter, config: LiveStartPushbackConfig | undefined, deps: LiveStartPushbackDeps, ptsSource?: PtsSource);
26
+ start(): void;
27
+ stop(): void;
28
+ private disarm;
29
+ private readonly onPlaying;
30
+ private readonly attempt;
31
+ /** The engine already began at the requested distance: report it measured, with no seek. */
32
+ private reportStartOffset;
33
+ private publishedEdge;
34
+ private skip;
35
+ }
36
+ export {};
@@ -0,0 +1 @@
1
+ import{contentSeekableEnd as e}from"./adPlayer/liveEdge";import{resolveLiveStartPushback as t}from"./resolveLiveStartPushback";export function resolveLiveStartEnablement(e,t){return"boolean"==typeof e?.enabled?{enabled:e.enabled,enabledBy:"config"}:{enabled:"anvatoCue"===t,enabledBy:"ptsSource"}}const i=e=>"number"==typeof e&&Number.isFinite(e)&&e>0,s=e=>Math.round(1e3*e)/1e3;export class LiveStartPushbackController{constructor(a,n,r,l){this.player=a,this.config=n,this.deps=r,this.armed=!1,this.playbackSeen=!1,this.startOffsetRequested=!1,this.generation=0,this.cleanupLanding=null,this.onPlaying=()=>{this.playbackSeen=!0,this.attempt()},this.attempt=()=>{if(!this.playbackSeen)return;if(!this.armed||this.deps.blocked()||this.player.paused||!1===this.player.playbackReady)return;const a=this.player.isLive??(this.player.duration===1/0||!Number.isFinite(this.player.duration)&&null);if(null===a)return;const n=this.startOffsetRequested;if(this.disarm(),this.startOffsetRequested=!1,!a)return;const r=this.player.targetDuration,l=i(r)?r:this.config?.targetDurationSeconds,o=this.config?.minimumDistanceTargetDurations??5,d=this.player.currentTime,u=e(this.player),h=this.publishedEdge(u),f=this.player.videoElement?.seekable,g=this.player.seekableStart??(f?.length?f.start(f.length-1):null),p={enabledBy:this.enablement.enabledBy,position:d,seekableStart:g,seekableEnd:u,liveEdge:h,liveEdgeSource:i(this.player.liveEdge)?"adapter":"seekable",targetDurationSec:i(l)?l:null,targetDurationSource:i(r)?"adapter":i(l)?"config":"unavailable",requestedOffsetTargetDurations:o,startOffsetRequested:n};let c=null;if(i(l)?i(o)&&Number.isFinite(l*o)?(null===h||null===g||!Number.isFinite(g)||h<=g||!Number.isFinite(d))&&(c="no-range"):c="invalid-config":c="no-target-duration",!c&&i(l)&&null!==h&&Object.assign(p,{initialOffsetSec:s(h-d),initialOffsetTargetDurations:s((h-d)/l),requestedOffsetSec:s(o*l)}),!this.enablement.enabled)return void this.skip("disabled",p);if(c||!i(l)||null===h||null===g)return void this.skip(c??"no-range",p);if(n&&h-d>=o*l-.25)return void this.reportStartOffset(d,h,l,o,p);const y=t(d,g,h,l,o);if(null===y)return void this.skip(h-d>=o*l?"already-behind":"no-backward-target",p);Object.assign(p,{target:y,rewindSec:s(d-y),rewindTargetDurations:s((d-y)/l),targetOffsetSec:s(h-y),targetOffsetTargetDurations:s((h-y)/l),windowClamped:y>h-o*l,method:"seek"});const b=this.generation;this.deps.setSeeking(!0);const m=t=>{if(b!==this.generation||!this.cleanupLanding)return;this.cleanupLanding();const i="seeked"===t?this.player.currentTime:null,a=this.publishedEdge(e(this.player)),n=null!==i&&Number.isFinite(i)&&null!==a?a-i:null;this.deps.diagnose("DA-LIVE-START-PUSHBACK-LANDED",`Live start landing: TD=${l}s, offset=${null===n?"unknown":`${s(n)}s (${s(n/l)}xTD)`}, ${t}`,{...p,reason:t,landed:i,liveEdgeAtLanding:a,landedOffsetSec:null===n?null:s(n),landedOffsetTargetDurations:null===n?null:s(n/l),errorSec:null===i?null:s(i-y)})},S=()=>m("seeked"),D=setTimeout(()=>m("timeout"),1e4);if(this.cleanupLanding=()=>{this.cleanupLanding=null,clearTimeout(D),this.player.off("seeked",S),this.deps.setSeeking(!1)},this.player.on("seeked",S),this.deps.diagnose("DA-LIVE-START-PUSHBACK",`Live start: TD=${l}s, offset=${p.initialOffsetSec}s (${p.initialOffsetTargetDurations}xTD), requested=${o*l}s (${o}xTD), rewind=${p.rewindSec}s (${p.rewindTargetDurations}xTD), target=${y}s`,p),b===this.generation)try{this.player.seek(y)}catch{m("seek-failed")}},this.enablement=resolveLiveStartEnablement(n,l)}start(){this.stop(),this.armed=!0,this.playbackSeen=!this.player.paused&&!1!==this.player.playbackReady;const e=this.config?.minimumDistanceTargetDurations??5,t=i(this.player.targetDuration)?this.player.targetDuration:i(this.config?.targetDurationSeconds)?this.config.targetDurationSeconds:null;this.startOffsetRequested=this.enablement.enabled&&i(e)&&!0===this.player.requestLiveStartOffset?.(e,t),this.player.on("playing",this.onPlaying),this.player.on("timeupdate",this.attempt),this.attempt()}stop(){this.generation++,this.disarm(),this.cleanupLanding?.()}disarm(){this.armed=!1,this.player.off("playing",this.onPlaying),this.player.off("timeupdate",this.attempt),this.startOffsetRequested&&this.player.requestLiveStartOffset?.(null,null)}reportStartOffset(e,t,i,a,n){const r=t-a*i,l=t-e,o={...n,target:s(r),rewindSec:0,rewindTargetDurations:0,targetOffsetSec:s(a*i),targetOffsetTargetDurations:s(a),windowClamped:!1,method:"start-offset"};this.deps.diagnose("DA-LIVE-START-PUSHBACK",`Live start: TD=${i}s, began ${s(l)}s (${s(l/i)}xTD) behind live via the engine start offset, requested=${s(a*i)}s (${a}xTD), no seek`,o),this.deps.diagnose("DA-LIVE-START-PUSHBACK-LANDED",`Live start landing: TD=${i}s, offset=${s(l)}s (${s(l/i)}xTD), start-offset`,{...o,reason:"start-offset",landed:e,liveEdgeAtLanding:t,landedOffsetSec:s(l),landedOffsetTargetDurations:s(l/i),errorSec:s(e-r)})}publishedEdge(e){const t=this.player.liveEdge;return!i(t)||t-this.player.currentTime>=86400?e:null===e?t:Math.max(t,e)}skip(e,t){this.deps.diagnose("DA-LIVE-START-PUSHBACK-SKIPPED",`Live start unchanged: ${e}, TD=${t.targetDurationSec??"unknown"}s, offset=${t.initialOffsetSec??"unknown"}s (${t.initialOffsetTargetDurations??"unknown"}xTD)`,{...t,reason:e})}}
@@ -23,15 +23,30 @@ export declare function anvatoPayloadFromCue(cue: TimedMetadataCue): string | nu
23
23
  * split on `&`, then on the FIRST `=`, `+` as space, percent-decode, raw text on failure, first duplicate wins.
24
24
  */
25
25
  export declare function parseAnvatoPayload(payload: string): Record<string, string>;
26
+ /** How far before its `startTime` a `ptsOffset`-placed cue may land (frame rounding between the two clocks). */
27
+ export declare const PTS_OFFSET_MIN_LEAD_SECONDS = 0.1;
28
+ /** How far after its `startTime` (the carrying segment's start) a `ptsOffset`-placed cue may land. */
29
+ export declare const PTS_OFFSET_MAX_LAG_SECONDS = 15;
30
+ /** Structured-diagnostic sink for the tracker. Optional; absent means silent. */
31
+ export type CueTrackerDiagnose = (code: 'DA-ANVATO-CUE-SEGMENT-ANCHORED' | 'DA-ANVATO-CUE-OFFSET-REJECTED', message: string, context: Record<string, unknown>) => void;
26
32
  /**
27
33
  * Maps a break's manifest `pts` to the media time its Anvato cue was observed at. With
28
34
  * `ptsSource: 'anvatoCue'` a PTS break is only schedulable once its cue was seen. Portable brain logic.
35
+ *
36
+ * A cue's `startTime` is only the cue's own PTS for ID3. A DASH emsg v0 with a zero delta reports
37
+ * the start of its carrying segment, so the adapter supplies `ptsOffset` and the cue is placed at
38
+ * `pts + ptsOffset`, provided that lands inside the carrying segment's plausible window.
29
39
  */
30
40
  export declare class MetadataCueTracker {
41
+ private readonly diagnose?;
31
42
  /** Observed cue `pts` parameter (seconds) → cue media time (seconds). */
32
43
  private mediaTimeByPts;
44
+ private segmentAnchoredDiagnosed;
45
+ private offsetRejectedPts;
46
+ constructor(diagnose?: CueTrackerDiagnose | undefined);
33
47
  /** Ignores non-Anvato cues, non-`type=cue` payloads, and non-finite `pts`. True when a cue was recorded or refreshed. */
34
48
  handleCue(cue: TimedMetadataCue): boolean;
49
+ private resolveMediaTime;
35
50
  /**
36
51
  * Media time of the cue matching `pts` within {@link PTS_MATCH_TOLERANCE_SECONDS}, or null.
37
52
  * Closest wins; ties resolve to the lower cue PTS so the result is observation-order independent.
@@ -1 +1 @@
1
- import{decodeUtf8 as e,parseId3GeobFrames as t}from"../id3/parseId3Geob";export const ANVATO_ID3_DESCRIPTION="Anvatos";export const ANVATO_ID3_MIME_TYPE="application/json";export const ANVATO_EMSG_SCHEME_ID_URI="urn:anvato:es1:052016";export const ID3_IN_EMSG_SCHEME_ID_URIS=["https://aomedia.org/emsg/ID3","https://developer.apple.com/streaming/emsg-id3"];export function isId3InEmsgScheme(e){return void 0!==e&&ID3_IN_EMSG_SCHEME_ID_URIS.includes(e)}export const PTS_MATCH_TOLERANCE_SECONDS=.005;export function isAnvatoCue(e){return"id3"===e.type?"Anvatos"===e.description&&(void 0===e.mimeType||"application/json"===e.mimeType):"emsg"===e.type&&"urn:anvato:es1:052016"===e.schemeIdUri}export function anvatoPayloadFromCue(n){if(isAnvatoCue(n))return n.messageData??null;if("emsg"===n.type&&isId3InEmsgScheme(n.schemeIdUri)&&n.raw)for(const o of t(n.raw))if("Anvatos"===o.description&&"application/json"===o.mimeType)return e(o.data);return null}export function parseAnvatoPayload(e){const t={};for(const o of e.split("&")){if(""===o)continue;const e=o.indexOf("="),i=-1===e?o:o.slice(0,e),s=-1===e?"":o.slice(e+1),r=n(i);r in t||(t[r]=n(s))}return t}function n(e){const t=e.replace(/\+/g," ");try{return decodeURIComponent(t)}catch{return t}}export class MetadataCueTracker{constructor(){this.mediaTimeByPts=new Map}handleCue(e){if(!Number.isFinite(e.startTime))return!1;const t=anvatoPayloadFromCue(e);if(!t)return!1;const n=parseAnvatoPayload(t);if("cue"!==n.type)return!1;const o=Number(n.pts);return!!Number.isFinite(o)&&(this.mediaTimeByPts.set(o,e.startTime),!0)}mediaTimeForPts(e){let t=null,n=1/0;for(const o of this.mediaTimeByPts.keys()){const i=Math.abs(o-e);i>.005||(i<n||i===n&&o<t)&&(t=o,n=i)}return null===t?null:this.mediaTimeByPts.get(t)}get size(){return this.mediaTimeByPts.size}clear(){this.mediaTimeByPts.clear()}}
1
+ import{decodeUtf8 as e,parseId3GeobFrames as t}from"../id3/parseId3Geob";export const ANVATO_ID3_DESCRIPTION="Anvatos";export const ANVATO_ID3_MIME_TYPE="application/json";export const ANVATO_EMSG_SCHEME_ID_URI="urn:anvato:es1:052016";export const ID3_IN_EMSG_SCHEME_ID_URIS=["https://aomedia.org/emsg/ID3","https://developer.apple.com/streaming/emsg-id3"];export function isId3InEmsgScheme(e){return void 0!==e&&ID3_IN_EMSG_SCHEME_ID_URIS.includes(e)}export const PTS_MATCH_TOLERANCE_SECONDS=.005;export function isAnvatoCue(e){return"id3"===e.type?"Anvatos"===e.description&&(void 0===e.mimeType||"application/json"===e.mimeType):"emsg"===e.type&&"urn:anvato:es1:052016"===e.schemeIdUri}export function anvatoPayloadFromCue(s){if(isAnvatoCue(s))return s.messageData??null;if("emsg"===s.type&&isId3InEmsgScheme(s.schemeIdUri)&&s.raw)for(const i of t(s.raw))if("Anvatos"===i.description&&"application/json"===i.mimeType)return e(i.data);return null}export function parseAnvatoPayload(e){const t={};for(const i of e.split("&")){if(""===i)continue;const e=i.indexOf("="),n=-1===e?i:i.slice(0,e),o=-1===e?"":i.slice(e+1),r=s(n);r in t||(t[r]=s(o))}return t}function s(e){const t=e.replace(/\+/g," ");try{return decodeURIComponent(t)}catch{return t}}export const PTS_OFFSET_MIN_LEAD_SECONDS=.1;export const PTS_OFFSET_MAX_LAG_SECONDS=15;export class MetadataCueTracker{constructor(e){this.diagnose=e,this.mediaTimeByPts=new Map,this.segmentAnchoredDiagnosed=!1,this.offsetRejectedPts=new Set}handleCue(e){if(!Number.isFinite(e.startTime))return!1;const t=anvatoPayloadFromCue(e);if(!t)return!1;const s=parseAnvatoPayload(t);if("cue"!==s.type)return!1;const i=Number(s.pts);return!!Number.isFinite(i)&&(this.mediaTimeByPts.set(i,this.resolveMediaTime(e,i)),!0)}resolveMediaTime(e,t){const s=e.ptsOffset;if("number"!=typeof s||!Number.isFinite(s))return"urn:anvato:es1:052016"!==e.schemeIdUri||this.segmentAnchoredDiagnosed||(this.segmentAnchoredDiagnosed=!0,this.diagnose?.("DA-ANVATO-CUE-SEGMENT-ANCHORED",`Anvato emsg cue pts=${t} has no ptsOffset; scheduling it at its segment start ${e.startTime}s, which can be up to one segment early.`,{pts:t,mediaTime:e.startTime})),e.startTime;const i=t=>t>=e.startTime-.1&&t<=e.startTime+15,n=t+s;if(i(n))return n;const o=t/1e3+s;return i(o)?o:(this.offsetRejectedPts.has(t)||(this.offsetRejectedPts.add(t),this.diagnose?.("DA-ANVATO-CUE-OFFSET-REJECTED",`Anvato cue pts=${t} maps to ${n}s with ptsOffset ${s}, outside its segment window from ${e.startTime}s; using the segment start.`,{pts:t,startTime:e.startTime,ptsOffset:s,mappedTime:n})),e.startTime)}mediaTimeForPts(e){let t=null,s=1/0;for(const i of this.mediaTimeByPts.keys()){const n=Math.abs(i-e);n>.005||(n<s||n===s&&i<t)&&(t=i,s=n)}return null===t?null:this.mediaTimeByPts.get(t)}get size(){return this.mediaTimeByPts.size}clear(){this.mediaTimeByPts.clear(),this.segmentAnchoredDiagnosed=!1,this.offsetRejectedPts.clear()}}
@@ -1 +1 @@
1
- import{debugLog as e}from"../diagnostics/debugLog";import{VendorAdError as t}from"../types/VendorAdError";export function isImaCsaiSdkAvailable(){return"undefined"!=typeof google&&void 0!==google.ima&&void 0!==google.ima.AdsLoader&&void 0!==google.ima.AdDisplayContainer}export class VastAdManager{constructor(e,t,s=!1){this.adContainer=e,this.adVideoElement=t,this.adDisplayContainer=null,this.adsLoader=null,this.adsManager=null,this.callbacks={},this.preloadResolve=null,this.preloadReject=null,this.startResolve=null,this.startReject=null,this.pendingMuted=!1,this.settled=!1,this.preloaded=!1,this.started=!1,this.currentAdDurationSec=null,this.debug=s}setCallbacks(e){this.callbacks=e}resize(){this.adsManager&&(this.adsManager.resize(this.slotWidth(),this.slotHeight(),google.ima.ViewMode.NORMAL),this.log("VAST AdsManager resized to",this.slotWidth(),"x",this.slotHeight()))}preloadVast(e,t){return this.reset(),isImaCsaiSdkAvailable()?new Promise((s,a)=>{this.preloadResolve=s,this.preloadReject=a;try{this.setupContainerAndLoader();const s=new google.ima.AdsRequest;s.adTagUrl=e,s.linearAdSlotWidth=this.slotWidth(),s.linearAdSlotHeight=this.slotHeight(),s.setAdWillAutoPlay(!0),this.pendingMuted=t?.muted??!1,s.setAdWillPlayMuted(this.pendingMuted),this.log("Preloading VAST ads:",e),this.adsLoader.requestAds(s)}catch(e){this.rejectPreload(e instanceof Error?e:new Error(String(e)))}}):Promise.reject(new Error('Google IMA client-side SDK not found. Include <script src="https://imasdk.googleapis.com/js/sdkloader/ima3.js"><\/script> in your page.'))}startPreloaded(){return new Promise((e,t)=>{if(this.adsManager&&this.preloaded&&!this.started){this.started=!0,this.settled=!1,this.startResolve=e,this.startReject=t;try{this.resize(),this.adsManager.start(),this.log("VAST AdsManager started")}catch(e){this.failStart(e instanceof Error?e:new Error(String(e)))}}else t(new Error("No preloaded VAST AdsManager; call preloadVast() first"))})}hasPreloadedManager(){return null!==this.adsManager&&this.preloaded&&!this.started&&!this.settled}setMuted(e){this.pendingMuted=e,this.adsManager?.setVolume(e?0:1)}setVolume(e){this.pendingMuted=e<=0,this.adsManager?.setVolume(e)}playVast(e,t){return this.preloadVast(e,t).then(()=>this.startPreloaded())}setupContainerAndLoader(){this.adDisplayContainer=new google.ima.AdDisplayContainer(this.adContainer,this.adVideoElement),this.adDisplayContainer.initialize(),this.adsLoader=new google.ima.AdsLoader(this.adDisplayContainer),this.adsLoader.addEventListener(google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED,e=>this.onAdsManagerLoaded(e)),this.adsLoader.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR,e=>this.onAdError(e))}onAdsManagerLoaded(e){try{const t=new google.ima.AdsRenderingSettings;t.restoreCustomPlaybackStateOnAdBreakComplete=!0,t.enablePreloading=!0;const s=e.getAdsManager(this.adVideoElement,t);this.adsManager=s,this.wireManagerEvents(s),this.pendingMuted&&s.setVolume(0),s.init(this.slotWidth(),this.slotHeight(),google.ima.ViewMode.NORMAL),this.preloaded=!0,this.resolvePreload(),this.log("VAST AdsManager initialized and ready")}catch(e){this.rejectPreload(e instanceof Error?e:new Error(String(e)))}}wireManagerEvents(e){const t=google.ima.AdEvent.Type;e.addEventListener(t.STARTED,e=>{this.resize();const t=e.getAd?.(),s=t?.getDuration?.();this.currentAdDurationSec="number"==typeof s&&Number.isFinite(s)&&s>0?s:null;const a=t?.getAdPodInfo?.()?.getAdPosition?.();this.callbacks.onAdStarted?.("number"==typeof a&&Number.isFinite(a)&&a>=1?Math.floor(a):void 0)}),e.addEventListener(t.AD_PROGRESS,()=>this.emitAdProgress()),e.addEventListener(t.FIRST_QUARTILE,()=>this.callbacks.onAdFirstQuartile?.()),e.addEventListener(t.MIDPOINT,()=>this.callbacks.onAdMidpoint?.()),e.addEventListener(t.THIRD_QUARTILE,()=>this.callbacks.onAdThirdQuartile?.()),e.addEventListener(t.CONTENT_PAUSE_REQUESTED,()=>this.callbacks.onContentPauseRequested?.()),e.addEventListener(t.CONTENT_RESUME_REQUESTED,()=>this.callbacks.onContentResumeRequested?.()),e.addEventListener(t.ALL_ADS_COMPLETED,()=>this.succeedStart()),e.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR,e=>this.onAdError(e))}emitAdProgress(){if(!this.callbacks.onAdProgress||!this.adsManager)return;const e=this.currentAdDurationSec;if(null==e)return;const t=this.adsManager.getRemainingTime();if(!Number.isFinite(t)||t<0)return;const s=Math.max(0,Math.min(e,e-t));this.callbacks.onAdProgress(s,e)}onAdError(e){const s=e?.getError?.(),a=s?.getMessage?.()??"Unknown IMA CSAI error",i=s?.getErrorCode?.();this.log("VAST IMA error:",a);const r="number"==typeof i?new t(a,String(i)):new Error(a);this.startReject?this.failStart(r):this.preloadReject&&this.rejectPreload(r)}succeedStart(){this.settled||(this.settled=!0,this.log("VAST playout complete"),this.startResolve?.(),this.clearStartPromises(),this.teardownManager())}failStart(e){this.settled||(this.settled=!0,this.startReject?.(e),this.clearStartPromises(),this.teardownManager())}resolvePreload(){this.preloadResolve?.(),this.clearPreloadPromises()}rejectPreload(e){this.preloadReject?.(e),this.clearPreloadPromises(),this.teardownManager()}clearPreloadPromises(){this.preloadResolve=null,this.preloadReject=null}clearStartPromises(){this.startResolve=null,this.startReject=null}slotWidth(){return this.adVideoElement.clientWidth||640}slotHeight(){return this.adVideoElement.clientHeight||360}teardownManager(){this.adsManager&&(this.adsManager.destroy(),this.adsManager=null),this.adsLoader&&(this.adsLoader.destroy(),this.adsLoader=null),this.adDisplayContainer&&(this.adDisplayContainer.destroy(),this.adDisplayContainer=null)}reset(){this.teardownManager(),this.settled=!1,this.preloaded=!1,this.started=!1,this.pendingMuted=!1,this.currentAdDurationSec=null,this.clearPreloadPromises(),this.clearStartPromises()}destroy(){this.reset(),this.callbacks={}}log(...t){this.debug&&e("[VastAdManager]",...t)}}
1
+ import{debugLog as e}from"../diagnostics/debugLog";import{AdError as t}from"../types/AdError";import{VendorAdError as s}from"../types/VendorAdError";import{classifyImaAdError as a}from"./classifyImaAdError";import{html5ImaErrorName as r}from"./html5ImaErrorNames";export function isImaCsaiSdkAvailable(){return"undefined"!=typeof google&&void 0!==google.ima&&void 0!==google.ima.AdsLoader&&void 0!==google.ima.AdDisplayContainer}export class VastAdManager{constructor(e,t,s=!1){this.adContainer=e,this.adVideoElement=t,this.adDisplayContainer=null,this.adsLoader=null,this.adsManager=null,this.callbacks={},this.preloadResolve=null,this.preloadReject=null,this.startResolve=null,this.startReject=null,this.pendingMuted=!1,this.settled=!1,this.preloaded=!1,this.started=!1,this.currentAdDurationSec=null,this.debug=s}setCallbacks(e){this.callbacks=e}resize(){this.adsManager&&(this.adsManager.resize(this.slotWidth(),this.slotHeight(),google.ima.ViewMode.NORMAL),this.log("VAST AdsManager resized to",this.slotWidth(),"x",this.slotHeight()))}preloadVast(e,s){return this.reset(),isImaCsaiSdkAvailable()?new Promise((t,a)=>{this.preloadResolve=t,this.preloadReject=a;try{this.setupContainerAndLoader();const t=new google.ima.AdsRequest;t.adTagUrl=e,t.linearAdSlotWidth=this.slotWidth(),t.linearAdSlotHeight=this.slotHeight(),t.setAdWillAutoPlay(!0),this.pendingMuted=s?.muted??!1,t.setAdWillPlayMuted(this.pendingMuted),this.log("Preloading VAST ads:",e),this.adsLoader.requestAds(t)}catch(e){this.rejectPreload(e instanceof Error?e:new Error(String(e)))}}):Promise.reject(new t('Google IMA client-side SDK not found. Include <script src="https://imasdk.googleapis.com/js/sdkloader/ima3.js"><\/script> in your page.',"DA-VAST-IMA-SDK-MISSING"))}startPreloaded(){return new Promise((e,t)=>{if(this.adsManager&&this.preloaded&&!this.started){this.started=!0,this.settled=!1,this.startResolve=e,this.startReject=t;try{this.resize(),this.adsManager.start(),this.log("VAST AdsManager started")}catch(e){this.failStart(e instanceof Error?e:new Error(String(e)))}}else t(new Error("No preloaded VAST AdsManager; call preloadVast() first"))})}hasPreloadedManager(){return null!==this.adsManager&&this.preloaded&&!this.started&&!this.settled}setMuted(e){this.pendingMuted=e,this.adsManager?.setVolume(e?0:1)}setVolume(e){this.pendingMuted=e<=0,this.adsManager?.setVolume(e)}playVast(e,t){return this.preloadVast(e,t).then(()=>this.startPreloaded())}setupContainerAndLoader(){this.adDisplayContainer=new google.ima.AdDisplayContainer(this.adContainer,this.adVideoElement),this.adDisplayContainer.initialize(),this.adsLoader=new google.ima.AdsLoader(this.adDisplayContainer),this.adsLoader.addEventListener(google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED,e=>this.onAdsManagerLoaded(e)),this.adsLoader.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR,e=>this.onAdError(e))}onAdsManagerLoaded(e){try{const t=new google.ima.AdsRenderingSettings;t.restoreCustomPlaybackStateOnAdBreakComplete=!0,t.enablePreloading=!0;const s=e.getAdsManager(this.adVideoElement,t);this.adsManager=s,this.wireManagerEvents(s),this.pendingMuted&&s.setVolume(0),s.init(this.slotWidth(),this.slotHeight(),google.ima.ViewMode.NORMAL),this.preloaded=!0,this.resolvePreload(),this.log("VAST AdsManager initialized and ready")}catch(e){this.rejectPreload(e instanceof Error?e:new Error(String(e)))}}wireManagerEvents(e){const t=google.ima.AdEvent.Type;e.addEventListener(t.STARTED,e=>{this.resize();const t=e.getAd?.(),s=t?.getDuration?.();this.currentAdDurationSec="number"==typeof s&&Number.isFinite(s)&&s>0?s:null;const a=t?.getAdPodInfo?.()?.getAdPosition?.();this.callbacks.onAdStarted?.("number"==typeof a&&Number.isFinite(a)&&a>=1?Math.floor(a):void 0)}),e.addEventListener(t.AD_PROGRESS,()=>this.emitAdProgress()),e.addEventListener(t.FIRST_QUARTILE,()=>this.callbacks.onAdFirstQuartile?.()),e.addEventListener(t.MIDPOINT,()=>this.callbacks.onAdMidpoint?.()),e.addEventListener(t.THIRD_QUARTILE,()=>this.callbacks.onAdThirdQuartile?.()),e.addEventListener(t.CONTENT_PAUSE_REQUESTED,()=>this.callbacks.onContentPauseRequested?.()),e.addEventListener(t.CONTENT_RESUME_REQUESTED,()=>this.callbacks.onContentResumeRequested?.()),e.addEventListener(t.ALL_ADS_COMPLETED,()=>this.succeedStart()),e.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR,e=>this.onAdError(e))}emitAdProgress(){if(!this.callbacks.onAdProgress||!this.adsManager)return;const e=this.currentAdDurationSec;if(null==e)return;const t=this.adsManager.getRemainingTime();if(!Number.isFinite(t)||t<0)return;const s=Math.max(0,Math.min(e,e-t));this.callbacks.onAdProgress(s,e)}onAdError(e){const i=e?.getError?.(),o=i?.getMessage?.()??"Unknown IMA CSAI error",d=i?.getErrorCode?.();this.log("VAST IMA error:",o);const n="number"==typeof d?new s(o,String(d),a(r(d))):new t(o,"DA-VAST-IMA-ERROR");this.startReject?this.failStart(n):this.preloadReject&&this.rejectPreload(n)}succeedStart(){this.settled||(this.settled=!0,this.log("VAST playout complete"),this.startResolve?.(),this.clearStartPromises(),this.teardownManager())}failStart(e){this.settled||(this.settled=!0,this.startReject?.(e),this.clearStartPromises(),this.teardownManager())}resolvePreload(){this.preloadResolve?.(),this.clearPreloadPromises()}rejectPreload(e){this.preloadReject?.(e),this.clearPreloadPromises(),this.teardownManager()}clearPreloadPromises(){this.preloadResolve=null,this.preloadReject=null}clearStartPromises(){this.startResolve=null,this.startReject=null}slotWidth(){return this.adVideoElement.clientWidth||640}slotHeight(){return this.adVideoElement.clientHeight||360}teardownManager(){this.adsManager&&(this.adsManager.destroy(),this.adsManager=null),this.adsLoader&&(this.adsLoader.destroy(),this.adsLoader=null),this.adDisplayContainer&&(this.adDisplayContainer.destroy(),this.adDisplayContainer=null)}reset(){this.teardownManager(),this.settled=!1,this.preloaded=!1,this.started=!1,this.pendingMuted=!1,this.currentAdDurationSec=null,this.clearPreloadPromises(),this.clearStartPromises()}destroy(){this.reset(),this.callbacks={}}log(...t){this.debug&&e("[VastAdManager]",...t)}}
@@ -58,6 +58,17 @@ export interface AdPlayerState {
58
58
  savedContentEdgeDistance: number | null;
59
59
  earlyReturnResumeOffsetSec: number | null;
60
60
  contentPositionAtBreakStart: number;
61
+ /**
62
+ * Whether this break's anchor is the in-stream cue rather than the pause position — i.e.
63
+ * whether the content under the ad is known slate of exactly the advertised duration.
64
+ */
65
+ resumeAnchorIsCue: boolean;
66
+ /**
67
+ * The seekable end as the engine reported it at break start, captured with the anchor so the
68
+ * two are comparable. Their difference is the cushion the resume has to work with: a playhead
69
+ * riding its engine's seekable end has none, and any under-published second then lands short.
70
+ */
71
+ resumeAnchorSeekableEnd: number | null;
61
72
  resumePrepositionedTo: number | null;
62
73
  resumePrepositionSettle: ReturnType<typeof setTimeout> | null;
63
74
  resumeCatchupActive: boolean;
@@ -1 +1 @@
1
- export function invalidateMediaPreload(e){return e.preparedMedia=null,++e.preloadGeneration}export function createAdPlayerState(){return{currentBreak:null,currentAsset:null,isPlaying:!1,contentPaused:!1,callbacks:null,durationTimer:null,startPlaybackCutoff:null,effectiveBreakDurationSec:null,adInsertionType:void 0,breakTimebase:null,adBreakCutSafetyMarginSec:0,detectedDeviceType:void 0,sessionAssetParameters:void 0,updatedAssetParameters:void 0,manifestAssetParameters:void 0,sessionAssetParameterMacros:{},pushedAssetParameters:null,gamStreamManager:null,gamPodMode:"none",gamPodTracker:null,gamPodAsset:null,earlyGamPodAdStart:null,withheldPodAdBegin:null,gamConfig:void 0,gamInitPromise:null,currentGamIdentity:void 0,playerContainer:null,stage:null,contentVideoElement:null,adContainer:null,adMediaContainer:null,adPlayer:null,companionElement:null,overlayImageEl:null,isContentFullscreen:!1,trackedContentVideo:null,fullscreenEnterHandler:null,fullscreenExitHandler:null,contentPictureInPicture:!1,pipTransferredToAd:!1,pipAdoptedForBreak:!1,sharedAdClaimActive:!1,savedContentSrc:null,savedContentTime:0,savedContentLive:!1,savedContentEdgeDistance:null,earlyReturnResumeOffsetSec:null,contentPositionAtBreakStart:0,resumePrepositionedTo:null,resumePrepositionSettle:null,resumeCatchupActive:!1,resumeCatchupRan:!1,lastResumeAlignRemainingSec:Number.POSITIVE_INFINITY,resumeAlignBlockedDiagnosed:!1,lastResumeSafeEnd:Number.NEGATIVE_INFINITY,resumeSawSeekDone:!1,resumeBestSawMarginSec:Number.NEGATIVE_INFINITY,resumeAlignTicks:0,resumeSawSteps:[],continueContentDuringBreak:!1,continueQualityOverrideApplied:!1,continueContentDuringBreakActive:!1,keepContentPlayingHandler:null,continuePlayoutStartWallMs:0,continuePlayoutTicker:null,continueRateWritten:null,continueRateWrittenAtMs:0,continueRateHonoured:!0,continueMaxAbsDriftSec:0,continueCorrections:[],continueDriftUnrecoverable:!1,continueRefusalReported:!1,contentMutedBeforeCatchup:null,resumeAlignCorrectionsMs:[],resumeLandedProbe:null,adSecondsPlayedThisBreak:0,currentAssetElapsedSec:0,preparedMedia:null,preloadGeneration:0,firstFrameGateActive:!1,firstFrameGateShown:!1,firstFrameResolver:null,firstFrameRejector:null,firstFrameTimeout:null,adStartDeadlineMs:null,adStartBudgetMs:0,vastAdManager:null}}
1
+ export function invalidateMediaPreload(e){return e.preparedMedia=null,++e.preloadGeneration}export function createAdPlayerState(){return{currentBreak:null,currentAsset:null,isPlaying:!1,contentPaused:!1,callbacks:null,durationTimer:null,startPlaybackCutoff:null,effectiveBreakDurationSec:null,adInsertionType:void 0,breakTimebase:null,adBreakCutSafetyMarginSec:0,detectedDeviceType:void 0,sessionAssetParameters:void 0,updatedAssetParameters:void 0,manifestAssetParameters:void 0,sessionAssetParameterMacros:{},pushedAssetParameters:null,gamStreamManager:null,gamPodMode:"none",gamPodTracker:null,gamPodAsset:null,earlyGamPodAdStart:null,withheldPodAdBegin:null,gamConfig:void 0,gamInitPromise:null,currentGamIdentity:void 0,playerContainer:null,stage:null,contentVideoElement:null,adContainer:null,adMediaContainer:null,adPlayer:null,companionElement:null,overlayImageEl:null,isContentFullscreen:!1,trackedContentVideo:null,fullscreenEnterHandler:null,fullscreenExitHandler:null,contentPictureInPicture:!1,pipTransferredToAd:!1,pipAdoptedForBreak:!1,sharedAdClaimActive:!1,savedContentSrc:null,savedContentTime:0,savedContentLive:!1,savedContentEdgeDistance:null,earlyReturnResumeOffsetSec:null,contentPositionAtBreakStart:0,resumeAnchorIsCue:!1,resumeAnchorSeekableEnd:null,resumePrepositionedTo:null,resumePrepositionSettle:null,resumeCatchupActive:!1,resumeCatchupRan:!1,lastResumeAlignRemainingSec:Number.POSITIVE_INFINITY,resumeAlignBlockedDiagnosed:!1,lastResumeSafeEnd:Number.NEGATIVE_INFINITY,resumeSawSeekDone:!1,resumeBestSawMarginSec:Number.NEGATIVE_INFINITY,resumeAlignTicks:0,resumeSawSteps:[],continueContentDuringBreak:!1,continueQualityOverrideApplied:!1,continueContentDuringBreakActive:!1,keepContentPlayingHandler:null,continuePlayoutStartWallMs:0,continuePlayoutTicker:null,continueRateWritten:null,continueRateWrittenAtMs:0,continueRateHonoured:!0,continueMaxAbsDriftSec:0,continueCorrections:[],continueDriftUnrecoverable:!1,continueRefusalReported:!1,contentMutedBeforeCatchup:null,resumeAlignCorrectionsMs:[],resumeLandedProbe:null,adSecondsPlayedThisBreak:0,currentAssetElapsedSec:0,preparedMedia:null,preloadGeneration:0,firstFrameGateActive:!1,firstFrameGateShown:!1,firstFrameResolver:null,firstFrameRejector:null,firstFrameTimeout:null,adStartDeadlineMs:null,adStartBudgetMs:0,vastAdManager:null}}
@@ -3,6 +3,10 @@ import { Asset, Break, BreakVariant, StaticAsset, VastAsset } from '../../types/
3
3
  import type { ResolvedPreloadMode } from '../detectPreloadMode';
4
4
  import { type AdPlayerState } from './AdPlayerState';
5
5
  import type { Clock } from '../BreakScheduler';
6
+ /** A stalled ad load gets one second load() this far into the ad-start budget. */
7
+ export declare const AD_LOAD_RETRY_AFTER_MS = 2000;
8
+ /** True when the ad element has produced nothing since load() — no metadata and no buffered media. */
9
+ export declare function adLoadShowsNoProgress(video: HTMLVideoElement | null | undefined): boolean;
6
10
  export interface AdPreloaderDeps {
7
11
  state: AdPlayerState;
8
12
  clock: Clock;
@@ -46,7 +50,11 @@ export declare class AdPreloader {
46
50
  revealFirstFrame(): void;
47
51
  armAdStartBudget(asset: Asset): void;
48
52
  remainingAdStartBudgetMs(): number;
49
- loadWithinBudget(load: Promise<void>): Promise<boolean>;
53
+ loadWithinBudget(startLoad: () => Promise<void>, context: {
54
+ breakId: string;
55
+ assetId: string;
56
+ uri: string;
57
+ }): Promise<boolean>;
50
58
  waitForFirstFrame(): Promise<void>;
51
59
  clearFirstFrame(reason?: string): void;
52
60
  clearFirstFrameResolver(): void;