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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/dist/OptiViewAds.d.ts +5 -4
  2. package/dist/OptiViewAds.js +1 -1
  3. package/dist/OptiViewAdsExternalVendors.d.ts +32 -0
  4. package/dist/external-playback/index.d.ts +4 -0
  5. package/dist/external-playback/index.js +1 -0
  6. package/dist/external-playback/observeExternalPlayback.d.ts +19 -0
  7. package/dist/external-playback/observeExternalPlayback.js +1 -0
  8. package/dist/external-playback/reconnectHold.d.ts +19 -0
  9. package/dist/external-playback/reconnectHold.js +1 -0
  10. package/dist/generated/diagnosticCodes.d.ts +46 -6
  11. package/dist/generated/diagnosticCodes.js +1 -1
  12. package/dist/generated/version.d.ts +1 -1
  13. package/dist/generated/version.js +1 -1
  14. package/dist/index.d.ts +7 -5
  15. package/dist/index.js +1 -1
  16. package/dist/interfaces/AdSourceQueue.d.ts +19 -0
  17. package/dist/interfaces/AdSourceQueue.js +1 -0
  18. package/dist/interfaces/PlayerAdapter.d.ts +31 -1
  19. package/dist/internal/index.d.ts +2 -0
  20. package/dist/internal/index.js +1 -1
  21. package/dist/services/AdBreakSequencer.d.ts +33 -4
  22. package/dist/services/AdBreakSequencer.js +1 -1
  23. package/dist/services/AdPlayerController.d.ts +12 -73
  24. package/dist/services/AdPlayerController.js +1 -1
  25. package/dist/services/BreakChain.d.ts +43 -0
  26. package/dist/services/BreakChain.js +1 -0
  27. package/dist/services/BreakChainRecorder.d.ts +63 -0
  28. package/dist/services/BreakChainRecorder.js +1 -0
  29. package/dist/services/BreakPreloadLedger.d.ts +13 -0
  30. package/dist/services/BreakPreloadLedger.js +1 -0
  31. package/dist/services/BreakScheduler.d.ts +20 -6
  32. package/dist/services/BreakScheduler.js +1 -1
  33. package/dist/services/BreakTransitionTimer.d.ts +27 -2
  34. package/dist/services/BreakTransitionTimer.js +1 -1
  35. package/dist/services/CmcdAdReporter.d.ts +1 -0
  36. package/dist/services/CmcdAdReporter.js +1 -1
  37. package/dist/services/ExternalPlaybackGate.d.ts +40 -0
  38. package/dist/services/ExternalPlaybackGate.js +1 -0
  39. package/dist/services/LiveStartPushbackController.d.ts +5 -10
  40. package/dist/services/LiveStartPushbackController.js +1 -1
  41. package/dist/services/ManifestService.d.ts +2 -1
  42. package/dist/services/ManifestService.js +1 -1
  43. package/dist/services/ReloadGuard.d.ts +11 -0
  44. package/dist/services/ReloadGuard.js +1 -0
  45. package/dist/services/RewindRearm.d.ts +3 -1
  46. package/dist/services/RewindRearm.js +1 -1
  47. package/dist/services/SsaiController.js +1 -1
  48. package/dist/services/adPlayer/AdPlayerCallbacks.d.ts +63 -0
  49. package/dist/services/adPlayer/AdPlayerCallbacks.js +1 -0
  50. package/dist/services/adPlayer/AdPlayerEvents.d.ts +3 -7
  51. package/dist/services/adPlayer/AdPlayerEvents.js +1 -1
  52. package/dist/services/adPlayer/AdPlayerState.d.ts +0 -1
  53. package/dist/services/adPlayer/AdPlayerState.js +1 -1
  54. package/dist/services/adPlayer/AdPreloader.d.ts +2 -2
  55. package/dist/services/adPlayer/AdPreloader.js +1 -1
  56. package/dist/services/adPlayer/AdStage.d.ts +7 -4
  57. package/dist/services/adPlayer/AdStage.js +1 -1
  58. package/dist/services/adPlayer/AssetParameterResolver.d.ts +10 -2
  59. package/dist/services/adPlayer/AssetParameterResolver.js +1 -1
  60. package/dist/services/adPlayer/AssetPlayback.d.ts +9 -2
  61. package/dist/services/adPlayer/AssetPlayback.js +1 -1
  62. package/dist/services/adPlayer/AudioController.d.ts +58 -0
  63. package/dist/services/adPlayer/AudioController.js +1 -0
  64. package/dist/services/adPlayer/BreakSequenceDriver.d.ts +9 -3
  65. package/dist/services/adPlayer/BreakSequenceDriver.js +1 -1
  66. package/dist/services/adPlayer/ContentResumeCoordinator.d.ts +2 -0
  67. package/dist/services/adPlayer/ContentResumeCoordinator.js +1 -1
  68. package/dist/services/adPlayer/GamPodBridge.d.ts +11 -1
  69. package/dist/services/adPlayer/GamPodBridge.js +1 -1
  70. package/dist/services/adPlayer/PauseAdPresenter.d.ts +3 -1
  71. package/dist/services/adPlayer/PauseAdPresenter.js +1 -1
  72. package/dist/services/adPlayer/PreparedPresentation.js +1 -1
  73. package/dist/services/adPlayer/PresentationModeTracker.d.ts +1 -1
  74. package/dist/services/adPlayer/PresentationModeTracker.js +1 -1
  75. package/dist/services/adPlayer/SharedElementBreakPlayer.d.ts +3 -0
  76. package/dist/services/adPlayer/SharedElementBreakPlayer.js +1 -1
  77. package/dist/services/adPlayer/SuccessorQueue.d.ts +36 -0
  78. package/dist/services/adPlayer/SuccessorQueue.js +1 -0
  79. package/dist/services/adPlayer/SurfaceRevealReporter.d.ts +28 -0
  80. package/dist/services/adPlayer/SurfaceRevealReporter.js +1 -0
  81. package/dist/services/adPlayer/presentation/DomPresentationHost.d.ts +22 -0
  82. package/dist/services/adPlayer/presentation/DomPresentationHost.js +1 -0
  83. package/dist/services/adPlayer/presentation/LayoutTransform.d.ts +28 -0
  84. package/dist/services/adPlayer/presentation/LayoutTransform.js +1 -0
  85. package/dist/services/adPlayer/presentation/PresentationLayout.d.ts +19 -0
  86. package/dist/services/adPlayer/presentation/PresentationLayout.js +1 -0
  87. package/dist/services/assetParameterMacroTable.js +1 -1
  88. package/dist/services/assetParameterMacros.d.ts +9 -9
  89. package/dist/services/assetParameterMacros.js +1 -1
  90. package/dist/services/channelSwitch.d.ts +2 -2
  91. package/dist/services/externalPlaybackDiagnostic.d.ts +3 -0
  92. package/dist/services/externalPlaybackDiagnostic.js +1 -0
  93. package/dist/services/externalPlaybackGuard.d.ts +24 -0
  94. package/dist/services/externalPlaybackGuard.js +1 -0
  95. package/dist/services/parseBreakManifest.d.ts +2 -1
  96. package/dist/services/parseBreakManifest.js +1 -1
  97. package/dist/services/resolveChannelEvents.d.ts +17 -0
  98. package/dist/services/resolveChannelEvents.js +1 -0
  99. package/dist/services/resolveExternalVendorConfigurations.js +1 -1
  100. package/dist/services/resolveGamVendorConfiguration.d.ts +6 -1
  101. package/dist/services/resolveGamVendorConfiguration.js +1 -1
  102. package/dist/services/ssbStream.d.ts +39 -0
  103. package/dist/services/ssbStream.js +1 -0
  104. package/dist/services/ssbSwitchSignals.d.ts +15 -0
  105. package/dist/services/ssbSwitchSignals.js +1 -0
  106. package/dist/services/staticAssetUrl.d.ts +28 -0
  107. package/dist/services/staticAssetUrl.js +1 -0
  108. package/dist/session/AdBreakStatusTracker.d.ts +18 -0
  109. package/dist/session/AdBreakStatusTracker.js +1 -1
  110. package/dist/session/BreakLifecycle.d.ts +6 -0
  111. package/dist/session/BreakLifecycle.js +1 -1
  112. package/dist/session/SessionStarter.d.ts +6 -43
  113. package/dist/session/SessionStarter.js +1 -1
  114. package/dist/session/SessionStarterDeps.d.ts +39 -0
  115. package/dist/session/SessionStarterDeps.js +1 -0
  116. package/dist/session/SessionStarterHelpers.d.ts +36 -0
  117. package/dist/session/SessionStarterHelpers.js +1 -0
  118. package/dist/session/SessionState.d.ts +22 -2
  119. package/dist/session/SessionState.js +1 -1
  120. package/dist/session/SsbSwitch.d.ts +55 -0
  121. package/dist/session/SsbSwitch.js +1 -0
  122. package/dist/session/breakMeasurement.d.ts +16 -0
  123. package/dist/session/breakMeasurement.js +1 -0
  124. package/dist/session/contentSnapback.d.ts +13 -0
  125. package/dist/session/contentSnapback.js +1 -0
  126. package/dist/session/createExternalPlaybackGate.d.ts +19 -0
  127. package/dist/session/createExternalPlaybackGate.js +1 -0
  128. package/dist/session/sessionTick.d.ts +7 -0
  129. package/dist/session/sessionTick.js +1 -0
  130. package/dist/types/BreakManifest.d.ts +58 -5
  131. package/dist/types/Events.d.ts +21 -10
  132. package/dist/types/ExternalVendorConfiguration.d.ts +3 -1
  133. package/dist/types/OptiViewAdsConfig.d.ts +24 -8
  134. package/package.json +5 -1
@@ -41,10 +41,8 @@ export declare class OptiViewAds {
41
41
  static get version(): string;
42
42
  private player;
43
43
  private readonly liveStartPushback;
44
- /** Releases the scheduler's reload guard after a content error — see onContentError. */
45
- private contentErrorReloadGuard;
46
- /** How long a content error brackets scheduler position samples as reload noise. */
47
- private readonly CONTENT_ERROR_RELOAD_GUARD_MS;
44
+ /** A content error brackets scheduler position samples as reload noise for 5s — see onContentError. */
45
+ private readonly contentErrorReloadGuard;
48
46
  private manifestService?;
49
47
  private breakScheduler?;
50
48
  private adPlayerController?;
@@ -66,6 +64,7 @@ export declare class OptiViewAds {
66
64
  private sessionStarter;
67
65
  private readonly adBreakStatusTracker;
68
66
  private breakTransitionTimer?;
67
+ private breakChainRecorder?;
69
68
  private readonly onContentSeeked;
70
69
  private readonly onContentWaiting;
71
70
  private readonly onContentPlaying;
@@ -78,6 +77,8 @@ export declare class OptiViewAds {
78
77
  /** Break ids already reported as newly seen on DA-MANIFEST-POLLED (see notedManifestBreakIds). */
79
78
  private seenManifestBreakIds;
80
79
  private readonly onTimedMetadata;
80
+ private readonly externalPlayback;
81
+ private ssbSwitch;
81
82
  constructor(config: OptiViewAdsConfig);
82
83
  /** SSAI-mode construction: no manifest service, break scheduler, or ad-player overlay. */
83
84
  private initSsai;
@@ -1 +1 @@
1
- import{DEFAULT_MODE as e,DEFAULT_AD_PRELOAD_MODE as t,DEFAULT_AD_BREAK_CONTENT_RECOVERY as s,DEFAULT_AD_INSERTION_MODE as r,DEFAULT_AD_BREAK_CUT_SAFETY_MARGIN_SEC as i,DEFAULT_DOUBLE_BOX_AUDIO as a,resolveChaining as o,resolveTuneIn as n,resolveBreakWarnings as d,resolvePdtGraceSeconds as l}from"./types/OptiViewAdsConfig";import{SsaiController as h}from"./services/SsaiController";import{resolvePreloadMode as c}from"./services/detectPreloadMode";import{resolveContentRecovery as u}from"./services/detectContentRecovery";import{emitResolvedModeDiagnostics as m}from"./services/resolvedModeDiagnostics";import{isSingleSurfaceWebDevice as p,resolveInsertionMode as k}from"./services/detectInsertionMode";import{ManifestService as g}from"./services/ManifestService";import{BreakScheduler as f,systemClock as y}from"./services/BreakScheduler";import{MetadataCueTracker as S}from"./services/MetadataCueTracker";import{armBreakFromPresentedCue as v,diagnoseAnvatoCueObserved as C,notedManifestBreakIds as A,wireCueResume as b}from"./services/anvatoCueRuntime";import{AdPlayerController as P}from"./services/AdPlayerController";import{effectiveFormat as T}from"./services/pictureInPicture";import{resolveBreakFormat as B}from"./services/AdBreakSequencer";import{connectPauseAd as w}from"./services/adPlayer/connectPauseAd";import{BreakTransitionTimer as I}from"./services/BreakTransitionTimer";import{LensReporter as R}from"./services/LensReporter";import{CmcdAdReporter as D}from"./services/CmcdAdReporter";import{DiagnosticBuffer as E}from"./diagnostics/DiagnosticBuffer";import{recordEventTimeline as O}from"./diagnostics/recordEventTimeline";import{DIAGNOSTIC_CODES as M}from"./diagnostics/errorCodes";import{SDK_VERSION as x}from"./generated/version";import{playbackErrorContext as L}from"./diagnostics/playbackErrorContext";import{debugLog as V}from"./diagnostics/debugLog";import{AdBreakStatusTracker as G}from"./session/AdBreakStatusTracker";import{BreakLifecycle as H}from"./session/BreakLifecycle";import{SessionStarter as F}from"./session/SessionStarter";import{SessionState as q}from"./session/SessionState";import{LiveStartPushbackController as N}from"./services/LiveStartPushbackController";import{buildDiagnosticReport as K}from"./services/diagnosticReport";import{toPlaybackError as $}from"./services/toPlaybackError";import{readExternalVendorConfiguration as _}from"./services/externalVendorConfigurationGetter";import{createExternalVendorConfigurationTracker as U}from"./services/ExternalVendorConfigurationTracker";export class OptiViewAds{static get version(){return x}constructor(h){if(this.contentErrorReloadGuard=null,this.CONTENT_ERROR_RELOAD_GUARD_MS=5e3,this.eventHandlers=new Map,this.diagnosticHandlers=new Set,this.destroyed=!1,this.externalVendorConfigurationTracker=U(e=>this.emit("externalvendorconfigurationchange",e)),this.continueContentDuringBreak=!1,this.state=new q,this.metadataCueTracker=null,this.anvatoObservedLogged=new Set,this.seenManifestBreakIds=new Set,this.player=h.player,this.liveStartPushback=new N(this.player,h.liveStartPushback,{blocked:()=>!this.state.sessionActive||this.state.channelSwitchInFlight||!!this.adPlayerController?.isAdPlaying()||!!this.state.heldImmediatePreRollId,setSeeking:e=>this.breakScheduler?.setStartupSeeking(e),diagnose:(e,t,s)=>this.diagnose(e,t,s)},h.ptsSource),this.debug=h.debug??!1,this.mode=h.mode??e,this.hasExplicitMode=void 0!==h.mode,this.lensReporter=new R((...e)=>this.log(...e)),this.cmcdAdReporter=new D(()=>this.lensReporter.handle,(...e)=>this.log(...e)),this.adBreakStatusTracker=new G({emit:(e,t)=>this.emit(e,t),effectiveFormatOf:e=>this.effectiveFormatOf(e),secondsUntilBreak:e=>this.breakScheduler?.getSecondsUntilBreak(e)??void 0,breakTransitionTimer:()=>this.breakTransitionTimer}),this.onContentSeeked=()=>{const e=this.breakScheduler?.getActiveBreak();if(!e?.controls?.snapback)return;if(this.state.snapbackSeekInFlight)return void(this.state.snapbackSeekInFlight=!1);const t="number"==typeof e.start?e.start:this.player.currentTime;Math.abs(this.player.currentTime-t)<=.5||(this.log("Snapback: seek detected during locked break, snapping back to break start"),this.state.snapbackSeekInFlight=!0,this.player.seek(t))},this.onContentWaiting=()=>{this.adPlayerController?.isAdPlaying()||this.emit("waiting",{type:"waiting",timestamp:Date.now(),source:"content"})},this.onContentPlaying=()=>{this.adPlayerController?.isAdPlaying()||(this.breakScheduler?.markPlaybackStart(),this.breakTransitionTimer?.settle("out-of-break"),!1!==this.pauseAdController?.onResume()?this.emit("playing",{type:"playing",timestamp:Date.now(),source:"content"}):this.player.pause())},this.onContentPause=()=>{this.adPlayerController?.isAdPlaying()||this.breakScheduler?.isAdStartDelayActive()||this.pauseAdController?.onPause()},this.onContentEnded=()=>{this.adPlayerController?.isAdPlaying()||this.breakScheduler?.markPlaybackEnd()},this.onContentError=e=>{this.sessionStarter?.cancelHeldImmediatePreRollOnContentError();const t=$(e,"Content playback error");this.diagnose("DA-CONTENT-PLAYBACK-ERROR",t.message,L(e)),this.breakScheduler?.setContentReloading(!0),this.contentErrorReloadGuard&&clearTimeout(this.contentErrorReloadGuard),this.contentErrorReloadGuard=setTimeout(()=>{this.contentErrorReloadGuard=null,this.breakScheduler?.setContentReloading(!1)},this.CONTENT_ERROR_RELOAD_GUARD_MS),this.emit("aderror",{type:"aderror",timestamp:Date.now(),source:"content",error:t})},this.onTimedMetadata=e=>{if(!e)return;const t=e,s=this.metadataCueTracker?.handleCue(t)??!1;this.metadataCueTracker&&this.adPlayerController?.ingestStreamClockCue(t),C(t,s,this.metadataCueTracker,this.anvatoObservedLogged,this.player.currentTime,(e,t,s)=>this.diagnose(e,t,s)),"presented"===t.phase&&v(t,this.metadataCueTracker,this.breakScheduler)},this.continueContentDuringBreak=h.continueContentDuringBreak??!1,"ssai"===this.mode)return void this.initSsai(h);if(!h.createAdAdapter)throw new Error("[OptiViewAds] createAdAdapter is required. Use the @dolby-optiview/ads-sdk package for a default HLS.js ad player, or supply your own createAdAdapter factory.");this.state.steeredSsaiOptions={maxBitrate:h.maxBitrate,autoplay:h.autoplay??!1,streamActivityMonitorId:h.gam?.streamActivityMonitorId};const T=c(h.adPreload??t),B=h.adBreakContentRecovery??s,O=u(B),M=k(h.adInsertion??r),x=o(h.chaining),V=n(h.tuneIn),K=d(h.breakWarnings),_=l(h.pdtGraceSeconds);"anvatoCue"===h.ptsSource&&(this.metadataCueTracker=new S(this.diagnose.bind(this))),this.diagnosticBuffer=new E(h.diagnostics?.bufferSize),this.breakTransitionTimer=new I(e=>{this.diagnose("DA-BREAK-TRANSITION",`Transition ${e.kind} took ${e.durationMs}ms`,{transition:e.kind,durationMs:e.durationMs,breakId:e.breakId,adPreload:T,adInsertion:M,...e.phases?{phases:e.phases}:{}})}),this.state.configSummary={mode:this.mode,adPreload:T,adBreakContentRecovery:O,adInsertion:M,chaining:{enabled:x.enabled,maxGapSeconds:x.maxGapSeconds},tuneIn:{enabled:V.enabled,minBreakDurationSeconds:V.minBreakDurationSeconds},breakWarnings:{seconds:K.seconds},ptsSource:h.ptsSource??"mediaTime",continueContentDuringBreak:this.continueContentDuringBreak,gam:h.gam?{hasAssetParameters:!1,streamActivityMonitorId:h.gam.streamActivityMonitorId}:void 0},m({adPreload:h.adPreload??t,adInsertion:h.adInsertion??r,adBreakContentRecovery:B},{preloadMode:T,insertionMode:M,contentRecovery:O},(e,t,s)=>this.diagnose(e,t,s)),this.manifestService=new g(this.debug,h.interceptManifestResponse,(e,t,s)=>this.diagnose(e,t,s),h.interceptManifestRequest),this.breakScheduler=new f(this.player,this.debug,x,void 0,V,(e,t,s)=>this.diagnose(e,t,s),K,this.metadataCueTracker,_),this.adPlayerController=new P(h.player,h.container,h.playerContainer,h.createAdAdapter,this.debug,h.gam,T,M,h.adBreakCutSafetyMarginSec??i,h.doubleBoxAudio??a,p("undefined"!=typeof navigator?navigator.userAgent:"","undefined"!=typeof navigator?navigator.maxTouchPoints??0:0),y,O),this.adPlayerController.setTransitionPhaseReporter(this.debug?e=>this.breakTransitionTimer?.phase(e):null),b(this.adPlayerController,()=>this.metadataCueTracker),this.adPlayerController.setChainResolver(x.enabled?e=>this.breakScheduler.getChainedSuccessor(e):null),this.adPlayerController.initialize();const z=(e,t)=>void 0===e||void 0===t?{}:{adIndex:e,totalAds:t};this.adPlayerController.setGamEventCallbacks({onAdFirstQuartile:(e,t,s,r)=>{this.emit("adfirstquartile",{type:"adfirstquartile",timestamp:Date.now(),break:e,format:this.effectiveFormatOf(e),asset:t,...z(s,r)})},onAdMidpoint:(e,t,s,r)=>{this.emit("admidpoint",{type:"admidpoint",timestamp:Date.now(),break:e,format:this.effectiveFormatOf(e),asset:t,...z(s,r)})},onAdThirdQuartile:(e,t,s,r)=>{this.emit("adthirdquartile",{type:"adthirdquartile",timestamp:Date.now(),break:e,format:this.effectiveFormatOf(e),asset:t,...z(s,r)})}}),this.adPlayerController.setDiagnoseHandler((e,t,s)=>{this.diagnose(e,t,s)}),this.pauseAdController=w(this.adPlayerController,{debug:this.debug,diagnose:(e,t,s)=>this.diagnose(e,t,s)}),this.adPlayerController.setVolumeChangeCallback((e,t)=>{this.emit("volumechange",{type:"volumechange",timestamp:Date.now(),muted:e,volume:t})}),this.breakLifecycle=new H(this.state,{adPlayerController:this.adPlayerController,breakScheduler:this.breakScheduler,manifestService:this.manifestService,breakTransitionTimer:this.breakTransitionTimer,cmcdAdReporter:this.cmcdAdReporter,adBreakStatusTracker:this.adBreakStatusTracker,emit:(e,t)=>this.emit(e,t),diagnose:(e,t,s)=>this.diagnose(e,t,s),log:(...e)=>this.log(...e),effectiveFormatOf:e=>this.effectiveFormatOf(e),onBreakSettled:()=>this.sessionStarter?.applyPendingChannelSwitch()}),this.sessionStarter=new F(this.state,{player:this.player,debug:this.debug,mode:this.mode,hasExplicitMode:this.hasExplicitMode,continueContentDuringBreak:this.continueContentDuringBreak,startLivePushback:()=>this.liveStartPushback.start(),manifestService:this.manifestService,breakScheduler:this.breakScheduler,adPlayerController:this.adPlayerController,pauseAdController:this.pauseAdController,breakLifecycle:this.breakLifecycle,cmcdAdReporter:this.cmcdAdReporter,lensReporter:this.lensReporter,adBreakStatusTracker:this.adBreakStatusTracker,externalVendorConfigurationTracker:this.externalVendorConfigurationTracker,emit:(e,t)=>this.emit(e,t),diagnose:(e,t,s)=>this.diagnose(e,t,s),log:(...e)=>this.log(...e),endSessionInternal:e=>this.endSessionInternal(e),notedManifestBreakIds:e=>A(e,this.seenManifestBreakIds)});for(const[e,t]of this.contentListeners())this.player.on(e,t);this.log("OptiViewAds SDK initialized")}initSsai(e){if(!e.gam)throw new Error('[OptiViewAds] mode "ssai" requires a gam config.');if(!e.ssai?.networkCode)throw new Error('[OptiViewAds] mode "ssai" requires an ssai config (networkCode).');const t=e.ssai;this.diagnosticBuffer=new E(e.diagnostics?.bufferSize);const s=e.autoplay??!1;this.state.configSummary={mode:this.mode,adPreload:"n/a",adInsertion:"ssai",chaining:{enabled:!1,maxGapSeconds:0},tuneIn:{enabled:!1,minBreakDurationSeconds:0},gam:{networkCode:t.networkCode,hasAssetParameters:!1,streamActivityMonitorId:e.gam.streamActivityMonitorId},ssai:{maxBitrate:e.maxBitrate,autoplay:s},continueContentDuringBreak:this.continueContentDuringBreak},this.state.ssaiController=new h(this.player,{gam:e.gam,ssai:t,maxBitrate:e.maxBitrate,autoplay:s,debug:this.debug},{emit:(e,t)=>this.emit(e,t),diagnose:(e,t,s)=>this.diagnose(e,t,s),log:(...e)=>this.log(...e)}),this.player.on("volumechange",()=>{this.emit("volumechange",{type:"volumechange",timestamp:Date.now(),muted:this.player.muted,volume:this.player.volume})}),this.sessionStarter=new F(this.state,{player:this.player,debug:this.debug,mode:this.mode,hasExplicitMode:this.hasExplicitMode,continueContentDuringBreak:this.continueContentDuringBreak,startLivePushback:()=>this.liveStartPushback.start(),manifestService:this.manifestService,breakScheduler:this.breakScheduler,adPlayerController:this.adPlayerController,pauseAdController:this.pauseAdController,breakLifecycle:this.breakLifecycle,cmcdAdReporter:this.cmcdAdReporter,lensReporter:this.lensReporter,adBreakStatusTracker:this.adBreakStatusTracker,externalVendorConfigurationTracker:this.externalVendorConfigurationTracker,emit:(e,t)=>this.emit(e,t),diagnose:(e,t,s)=>this.diagnose(e,t,s),log:(...e)=>this.log(...e),endSessionInternal:e=>this.endSessionInternal(e),notedManifestBreakIds:e=>A(e,this.seenManifestBreakIds)});for(const[e,t]of this.contentListeners())this.player.on(e,t);this.log("OptiViewAds SDK initialized (SSAI mode)")}contentListeners(){const e=[["seeked",this.onContentSeeked],["waiting",this.onContentWaiting],["playing",this.onContentPlaying],["pause",this.onContentPause],["ended",this.onContentEnded],["error",this.onContentError]];return this.metadataCueTracker&&e.push(["timedmetadata",this.onTimedMetadata]),e}async startSession(e){return this.sessionStarter.startSession(e)}play(){return this.adPlayerController?.isAdPlaying()?(this.log("play() suppressed — ad break is active"),Promise.resolve()):this.player.play()}pause(){this.adPlayerController?.isAdPlaying()?this.log("pause() suppressed — ad break is active"):this.player.pause()}seek(e){if(!this.state.sessionActive)return;const t=this.breakScheduler?.getActiveBreak();t?.controls?.snapback?this.log("seek() suppressed — break has snapback enabled"):(this.breakScheduler?.notifyViewerSeek(),this.player.seek(e))}get muted(){return this.adPlayerController?this.adPlayerController.muted:this.player.muted}set muted(e){this.adPlayerController?this.adPlayerController.muted=e:this.player.muted=e}get volume(){return this.adPlayerController?this.adPlayerController.volume:this.player.volume}set volume(e){this.adPlayerController?this.adPlayerController.volume=e:this.player.volume=e}skipAd(){const e=this.adBreakStatusTracker.activeBreak;if(!this.state.sessionActive||!this.adPlayerController?.isAdPlaying()||!e)return this.log("skipAd() suppressed — no active ad break"),!1;const t=e.controls?.skipOffset;if(null==t)return this.log(`skipAd() suppressed — break '${e.id}' declares no controls.skipOffset`),this.diagnose("DA-AD-SKIP-SUPPRESSED","Skip requested but break declares no skipOffset",{breakId:e.id}),!1;const s=this.adBreakStatusTracker.activeBreakElapsedSec;return s<t?(this.log(`skipAd() suppressed — skipOffset ${t}s not reached (${s}s)`),this.diagnose("DA-AD-SKIP-SUPPRESSED","Skip requested before skipOffset",{breakId:e.id,skipOffset:t,elapsedSec:s}),!1):(this.log(`skipAd() — skipping break '${e.id}' at ${s}s (skipOffset ${t}s)`),this.diagnose("DA-AD-SKIPPED","Break skipped by viewer",{breakId:e.id,skipOffset:t,elapsedSec:s}),this.cmcdAdReporter.onSkipRequested(e.id),this.adPlayerController.skipBreak(),!0)}clickAd(){const e=this.adBreakStatusTracker.activeBreak,t=this.adPlayerController?.currentAdAsset()??null;if(!(this.state.sessionActive&&this.adPlayerController?.isAdPlaying()&&e&&t))return this.log("clickAd() suppressed — no ad on screen"),null;const s=t.interaction?.clickThrough;return this.diagnose("DA-AD-CLICKED","Ad clicked",{breakId:e.id,assetId:t.id,clickThrough:s??""}),this.emit("adclick",{type:"adclick",timestamp:Date.now(),break:e,format:this.effectiveFormatOf(e),asset:t,...s?{clickThrough:s}:{}}),s??null}endSession(){this.state.sessionActive&&this.endSessionInternal()}updateAssetParameters(e){if(!this.state.sessionActive)return void console.warn("[OptiViewAds] updateAssetParameters called but no active session.");const t="ssai"===this.mode||this.state.steeredSsaiActive;(t?this.state.ssaiController:this.adPlayerController).updateAssetParameters(e),this.log("Asset parameters updated"+(t?" (SSAI)":""))}updateAssetParameterMacros(e){if(!this.state.sessionActive)return void console.warn("[OptiViewAds] updateAssetParameterMacros called but no active session.");const t="ssai"===this.mode||this.state.steeredSsaiActive;(t?this.state.ssaiController:this.adPlayerController).updateAssetParameterMacros(e),this.log("Asset parameter macros updated"+(t?" (SSAI)":""))}getExternalVendorConfiguration(e){return _(this.state,this.externalVendorConfigurationTracker.entries,this.mode,this.adPlayerController,e)}endSessionInternal(e="end-session"){this.liveStartPushback.stop(),this.state.sessionActive=!1,this.state.sessionGeneration++,this.breakLifecycle?.reportAbandonedBreak(e),"ssai"===this.mode?this.state.ssaiController?.stop():(this.state.steeredSsaiActive&&(this.state.ssaiController?.stop(),delete this.state.ssaiController,this.state.steeredSsaiActive=!1),this.manifestService?.reset(),this.breakScheduler?.stop(),this.breakScheduler?.destroy(),this.adBreakStatusTracker.resetAdBreakStatus(),this.state.pauseAdTickInterval&&(clearInterval(this.state.pauseAdTickInterval),this.state.pauseAdTickInterval=null),this.sessionStarter?.clearHeldImmediatePreRollArm(),this.pauseAdController?.reset(),this.breakTransitionTimer?.reset(),this.adPlayerController?.stopActiveBreak(),this.adPlayerController?.releaseContentHold(),this.adPlayerController?.endGamSession()),this.state.clearSessionIdentity(),this.externalVendorConfigurationTracker.reset(),this.anvatoObservedLogged.clear(),this.seenManifestBreakIds.clear(),this.cmcdAdReporter.onSessionEnd(),this.lensReporter.endSession(),this.diagnose("DA-SESSION-ENDED","Session ended"),this.log("Session ended")}addEventListener(e,t){this.eventHandlers.has(e)||this.eventHandlers.set(e,new Set),this.eventHandlers.get(e).add(t)}removeEventListener(e,t){const s=this.eventHandlers.get(e);s&&s.delete(t)}emit(e,t){O(this.diagnosticBuffer,t),this.cmcdAdReporter.onEvent(t);const s=this.eventHandlers.get(e);if(s)for(const e of s)try{e(t)}catch(e){this.log("Event handler error:",e)}}onDiagnostic(e){this.diagnosticHandlers.add(e)}offDiagnostic(e){this.diagnosticHandlers.delete(e)}exportDiagnostics(){return K(this.state,this.diagnosticBuffer.getAll())}diagnose(e,t,s){if(this.destroyed)return;const r=M[e],i={ts:Date.now(),level:r?.level??"info",code:e,category:r?.category??"unknown",message:t,...s?{context:s}:{}};this.diagnosticBuffer.record(i);for(const e of this.diagnosticHandlers)try{e(i)}catch(e){this.log("Diagnostic handler error:",e)}this.debug&&V("[OptiViewAds]",e,t,s??"")}isAdPlaying(){return this.adPlayerController?.isAdPlaying()??!1}isPictureInPicture(){return this.adPlayerController?.isPictureInPicture()??!1}getPresentationState(){return{pictureInPicture:this.isPictureInPicture(),showing:this.adPlayerController?.isShowingAd()?"ad":"content",insertion:this.adPlayerController?.currentInsertionMode()??"overlay",windowOwner:this.adPlayerController?.pictureInPictureOwner()??null}}effectiveFormatOf(e){const t=this.adPlayerController?.declaredFormatOf(e)??B(e);return T(t,this.isPictureInPicture())}isSessionActive(){return this.state.sessionActive}getAdBreakStatus(){return this.adBreakStatusTracker.getAdBreakStatus()}destroy(){this.contentErrorReloadGuard&&(clearTimeout(this.contentErrorReloadGuard),this.contentErrorReloadGuard=null);for(const[e,t]of this.contentListeners())this.player.off(e,t);this.state.sessionActive&&this.endSessionInternal("destroy"),this.manifestService?.destroy(),this.pauseAdController?.destroy(),this.adPlayerController?.destroy(),this.state.ssaiController?.stop(),this.lensReporter.destroy(),this.eventHandlers.clear(),this.diagnosticHandlers.clear(),this.destroyed=!0,this.log("Destroyed")}log(...e){this.debug&&V("[OptiViewAds]",...e)}}
1
+ import{DEFAULT_MODE as e,DEFAULT_AD_PRELOAD_MODE as t,DEFAULT_AD_BREAK_CONTENT_RECOVERY as s,DEFAULT_AD_INSERTION_MODE as r,DEFAULT_AD_BREAK_CUT_SAFETY_MARGIN_SEC as i,DEFAULT_DOUBLE_BOX_AUDIO as a,resolveChaining as o,resolveTuneIn as n,resolveBreakWarnings as d,resolvePdtGraceSeconds as l}from"./types/OptiViewAdsConfig";import{SsaiController as h}from"./services/SsaiController";import{resolvePreloadMode as c}from"./services/detectPreloadMode";import{resolveContentRecovery as u}from"./services/detectContentRecovery";import{emitResolvedModeDiagnostics as m}from"./services/resolvedModeDiagnostics";import{isSingleSurfaceWebDevice as p,resolveInsertionMode as k}from"./services/detectInsertionMode";import{ManifestService as g}from"./services/ManifestService";import{BreakScheduler as y,systemClock as f}from"./services/BreakScheduler";import{MetadataCueTracker as S}from"./services/MetadataCueTracker";import{armBreakFromPresentedCue as v,diagnoseAnvatoCueObserved as C,notedManifestBreakIds as b,wireCueResume as A}from"./services/anvatoCueRuntime";import{AdPlayerController as P}from"./services/AdPlayerController";import{effectiveFormat as w}from"./services/pictureInPicture";import{ReloadGuard as B}from"./services/ReloadGuard";import{createExternalPlaybackGate as T}from"./session/createExternalPlaybackGate";import{SsbSwitchController as I}from"./session/SsbSwitch";import{createSnapbackHandler as D}from"./session/contentSnapback";import{resolveBreakFormat as R}from"./services/AdBreakSequencer";import{connectPauseAd as x}from"./services/adPlayer/connectPauseAd";import{createBreakMeasurement as E}from"./session/breakMeasurement";import{LensReporter as M}from"./services/LensReporter";import{CmcdAdReporter as O}from"./services/CmcdAdReporter";import{DiagnosticBuffer as L}from"./diagnostics/DiagnosticBuffer";import{recordEventTimeline as V}from"./diagnostics/recordEventTimeline";import{DIAGNOSTIC_CODES as H}from"./diagnostics/errorCodes";import{SDK_VERSION as G}from"./generated/version";import{playbackErrorContext as q}from"./diagnostics/playbackErrorContext";import{debugLog as F}from"./diagnostics/debugLog";import{AdBreakStatusTracker as K}from"./session/AdBreakStatusTracker";import{BreakLifecycle as $}from"./session/BreakLifecycle";import{SessionStarter as z}from"./session/SessionStarter";import{SessionState as N}from"./session/SessionState";import{LiveStartPushbackController as U}from"./services/LiveStartPushbackController";import{buildDiagnosticReport as W}from"./services/diagnosticReport";import{toPlaybackError as Q}from"./services/toPlaybackError";import{readExternalVendorConfiguration as j}from"./services/externalVendorConfigurationGetter";import{createExternalVendorConfigurationTracker as Y}from"./services/ExternalVendorConfigurationTracker";export class OptiViewAds{static get version(){return G}constructor(h){if(this.contentErrorReloadGuard=new B(e=>this.breakScheduler?.setContentReloading(e),5e3),this.eventHandlers=new Map,this.diagnosticHandlers=new Set,this.destroyed=!1,this.externalVendorConfigurationTracker=Y(e=>this.emit("externalvendorconfigurationchange",e)),this.continueContentDuringBreak=!1,this.state=new N,this.metadataCueTracker=null,this.anvatoObservedLogged=new Set,this.seenManifestBreakIds=new Set,this.player=h.player,this.liveStartPushback=new U(this.player,h.liveStartPushback,{blocked:()=>!this.state.sessionActive||this.state.channelSwitchInFlight||!!this.adPlayerController?.isAdPlaying()||!!this.state.heldImmediatePreRollId,setSeeking:e=>this.breakScheduler?.setStartupSeeking(e),diagnose:(e,t,s)=>this.diagnose(e,t,s)}),this.debug=h.debug??!1,this.mode=h.mode??e,this.hasExplicitMode=void 0!==h.mode,this.lensReporter=new M((...e)=>this.log(...e)),this.cmcdAdReporter=new O(()=>this.lensReporter.handle,(...e)=>this.log(...e)),this.externalPlayback=T(h.player,h.externalPlayback,{scheduler:()=>this.breakScheduler,diagnose:(e,t,s)=>this.diagnose(e,t,s),onAllowed:e=>this.ssbSwitch?.onAllowed(e),ssbSwitchPhase:()=>this.state.ssb?.phase??null}),this.adBreakStatusTracker=new K({externalPlaybackAllowed:()=>this.externalPlayback.managed?this.externalPlayback.isAllowedNow():void 0,deliveryMode:()=>this.state.sessionDeliveryMode??void 0,emit:(e,t)=>this.emit(e,t),effectiveFormatOf:e=>this.effectiveFormatOf(e),secondsUntilBreak:e=>this.breakScheduler?.getSecondsUntilBreak(e)??void 0,breakTransitionTimer:()=>this.breakTransitionTimer}),this.onContentSeeked=D({player:this.player,state:this.state,activeBreak:()=>this.breakScheduler?.getActiveBreak(),onViewerSeek:()=>this.sessionStarter?.discardPreparedGamSessionAfterSeek(),log:(...e)=>this.log(...e)}),this.onContentWaiting=()=>{this.adPlayerController?.isAdPlaying()||this.emit("waiting",{type:"waiting",timestamp:Date.now(),source:"content"})},this.onContentPlaying=()=>{this.adPlayerController?.isAdPlaying()||this.ssbSwitch?.isActive||(this.breakScheduler?.markPlaybackStart(),this.breakTransitionTimer?.settle("out-of-break"),!1!==this.pauseAdController?.onResume()?this.emit("playing",{type:"playing",timestamp:Date.now(),source:"content"}):this.player.pause())},this.onContentPause=()=>{this.adPlayerController?.isAdPlaying()||this.ssbSwitch?.isActive||this.breakScheduler?.isAdStartDelayActive()||this.pauseAdController?.onPause()},this.onContentEnded=()=>{this.adPlayerController?.isAdPlaying()||this.breakScheduler?.markPlaybackEnd()},this.onContentError=e=>{this.sessionStarter?.cancelHeldImmediatePreRollOnContentError();const t=Q(e,"Content playback error");this.diagnose("DA-CONTENT-PLAYBACK-ERROR",t.message,q(e)),this.contentErrorReloadGuard.arm(),this.emit("aderror",{type:"aderror",timestamp:Date.now(),source:"content",error:t})},this.onTimedMetadata=e=>{if(!e)return;const t=e,s=this.metadataCueTracker?.handleCue(t)??!1;this.metadataCueTracker&&this.adPlayerController?.ingestStreamClockCue(t),C(t,s,this.metadataCueTracker,this.anvatoObservedLogged,this.player.currentTime,(e,t,s)=>this.diagnose(e,t,s)),"presented"===t.phase&&v(t,this.metadataCueTracker,this.breakScheduler)},this.continueContentDuringBreak=h.continueContentDuringBreak??!1,"ssai"===this.mode)return void this.initSsai(h);if(!h.createAdAdapter)throw new Error("[OptiViewAds] createAdAdapter is required. Use the @dolby-optiview/ads-sdk package for a default HLS.js ad player, or supply your own createAdAdapter factory.");this.state.steeredSsaiOptions={maxBitrate:h.maxBitrate,autoplay:h.autoplay??!1,streamActivityMonitorId:h.gam?.streamActivityMonitorId};const w=c(h.adPreload??t),R=h.adBreakContentRecovery??s,V=u(R),H=k(h.adInsertion??r),G=o(h.chaining),F=n(h.tuneIn),W=d(h.breakWarnings),j=l(h.pdtGraceSeconds);"anvatoCue"===h.ptsSource&&(this.metadataCueTracker=new S(this.diagnose.bind(this))),this.diagnosticBuffer=new L(h.diagnostics?.bufferSize),({timer:this.breakTransitionTimer,recorder:this.breakChainRecorder}=E((e,t,s)=>this.diagnose(e,t,s),{adPreload:w,adInsertion:H})),this.state.configSummary={mode:this.mode,adPreload:w,adBreakContentRecovery:V,adInsertion:H,chaining:{enabled:G.enabled,maxGapSeconds:G.maxGapSeconds},tuneIn:{enabled:F.enabled,minBreakDurationSeconds:F.minBreakDurationSeconds},breakWarnings:{seconds:W.seconds},ptsSource:h.ptsSource??"mediaTime",continueContentDuringBreak:this.continueContentDuringBreak,gam:h.gam?{hasAssetParameters:!1,streamActivityMonitorId:h.gam.streamActivityMonitorId}:void 0},m({adPreload:h.adPreload??t,adInsertion:h.adInsertion??r,adBreakContentRecovery:R},{preloadMode:w,insertionMode:H,contentRecovery:V},(e,t,s)=>this.diagnose(e,t,s)),this.manifestService=new g(this.debug,h.interceptManifestResponse,(e,t,s)=>this.diagnose(e,t,s),h.interceptManifestRequest),this.breakScheduler=new y(this.player,this.debug,G,void 0,F,(e,t,s)=>this.diagnose(e,t,s),W,this.metadataCueTracker,j),this.adPlayerController=new P(h.player,h.container,h.playerContainer,h.createAdAdapter,this.debug,h.gam,w,H,h.adBreakCutSafetyMarginSec??i,h.doubleBoxAudio??a,p("undefined"!=typeof navigator?navigator.userAgent:"","undefined"!=typeof navigator?navigator.maxTouchPoints??0:0),f,V),this.adPlayerController.setTransitionPhaseReporter(this.debug?e=>this.breakTransitionTimer?.phase(e):null),A(this.adPlayerController,()=>this.metadataCueTracker),this.adPlayerController.setChainResolver(G.enabled?e=>this.breakScheduler.getChainedSuccessor(e):null),this.adPlayerController.initialize();const J=(e,t)=>void 0===e||void 0===t?{}:{adIndex:e,totalAds:t};this.adPlayerController.setGamEventCallbacks({onAdFirstQuartile:(e,t,s,r)=>{this.emit("adfirstquartile",{type:"adfirstquartile",timestamp:Date.now(),break:e,format:this.effectiveFormatOf(e),asset:t,...J(s,r)})},onAdMidpoint:(e,t,s,r)=>{this.emit("admidpoint",{type:"admidpoint",timestamp:Date.now(),break:e,format:this.effectiveFormatOf(e),asset:t,...J(s,r)})},onAdThirdQuartile:(e,t,s,r)=>{this.emit("adthirdquartile",{type:"adthirdquartile",timestamp:Date.now(),break:e,format:this.effectiveFormatOf(e),asset:t,...J(s,r)})}}),this.adPlayerController.setDiagnoseHandler((e,t,s)=>{this.diagnose(e,t,s)}),this.pauseAdController=x(this.adPlayerController,{debug:this.debug,diagnose:(e,t,s)=>this.diagnose(e,t,s)}),this.adPlayerController.setVolumeChangeCallback((e,t)=>{this.emit("volumechange",{type:"volumechange",timestamp:Date.now(),muted:e,volume:t})}),this.breakLifecycle=new $(this.state,{adPlayerController:this.adPlayerController,breakScheduler:this.breakScheduler,manifestService:this.manifestService,breakTransitionTimer:this.breakTransitionTimer,breakChainRecorder:this.breakChainRecorder,cmcdAdReporter:this.cmcdAdReporter,adBreakStatusTracker:this.adBreakStatusTracker,emit:(e,t)=>this.emit(e,t),diagnose:(e,t,s)=>this.diagnose(e,t,s),log:(...e)=>this.log(...e),effectiveFormatOf:e=>this.effectiveFormatOf(e),onBreakSettled:()=>this.sessionStarter?.applyPendingChannelSwitch(),onBreakEnded:()=>this.externalPlayback.handleBreakEnded()});const X={player:this.player,debug:this.debug,mode:this.mode,hasExplicitMode:this.hasExplicitMode,continueContentDuringBreak:this.continueContentDuringBreak,startLivePushback:()=>this.liveStartPushback.start(),manifestService:this.manifestService,breakScheduler:this.breakScheduler,adPlayerController:this.adPlayerController,pauseAdController:this.pauseAdController,breakLifecycle:this.breakLifecycle,cmcdAdReporter:this.cmcdAdReporter,lensReporter:this.lensReporter,adBreakStatusTracker:this.adBreakStatusTracker,externalPlayback:this.externalPlayback,externalVendorConfigurationTracker:this.externalVendorConfigurationTracker,emit:(e,t)=>this.emit(e,t),diagnose:(e,t,s)=>this.diagnose(e,t,s),log:(...e)=>this.log(...e),endSessionInternal:e=>this.endSessionInternal(e),notedManifestBreakIds:e=>b(e,this.seenManifestBreakIds)};this.sessionStarter=new z(this.state,X),this.ssbSwitch=new I(this.state,this.sessionStarter,X);for(const[e,t]of this.contentListeners())this.player.on(e,t);this.log("OptiViewAds SDK initialized")}initSsai(e){if(!e.gam)throw new Error('[OptiViewAds] mode "ssai" requires a gam config.');if(!e.ssai?.networkCode)throw new Error('[OptiViewAds] mode "ssai" requires an ssai config (networkCode).');const t=e.ssai;this.diagnosticBuffer=new L(e.diagnostics?.bufferSize);const s=e.autoplay??!1;this.state.configSummary={mode:this.mode,adPreload:"n/a",adInsertion:"ssai",chaining:{enabled:!1,maxGapSeconds:0},tuneIn:{enabled:!1,minBreakDurationSeconds:0},gam:{networkCode:t.networkCode,hasAssetParameters:!1,streamActivityMonitorId:e.gam.streamActivityMonitorId},ssai:{maxBitrate:e.maxBitrate,autoplay:s},continueContentDuringBreak:this.continueContentDuringBreak},this.state.ssaiController=new h(this.player,{gam:e.gam,ssai:t,maxBitrate:e.maxBitrate,autoplay:s,debug:this.debug},{emit:(e,t)=>this.emit(e,t),diagnose:(e,t,s)=>this.diagnose(e,t,s),log:(...e)=>this.log(...e)}),this.player.on("volumechange",()=>{this.emit("volumechange",{type:"volumechange",timestamp:Date.now(),muted:this.player.muted,volume:this.player.volume})}),this.sessionStarter=new z(this.state,{player:this.player,debug:this.debug,mode:this.mode,hasExplicitMode:this.hasExplicitMode,continueContentDuringBreak:this.continueContentDuringBreak,startLivePushback:()=>this.liveStartPushback.start(),manifestService:this.manifestService,breakScheduler:this.breakScheduler,adPlayerController:this.adPlayerController,pauseAdController:this.pauseAdController,breakLifecycle:this.breakLifecycle,cmcdAdReporter:this.cmcdAdReporter,lensReporter:this.lensReporter,externalPlayback:this.externalPlayback,adBreakStatusTracker:this.adBreakStatusTracker,externalVendorConfigurationTracker:this.externalVendorConfigurationTracker,emit:(e,t)=>this.emit(e,t),diagnose:(e,t,s)=>this.diagnose(e,t,s),log:(...e)=>this.log(...e),endSessionInternal:e=>this.endSessionInternal(e),notedManifestBreakIds:e=>b(e,this.seenManifestBreakIds)});for(const[e,t]of this.contentListeners())this.player.on(e,t);this.log("OptiViewAds SDK initialized (SSAI mode)")}contentListeners(){const e=[["seeked",this.onContentSeeked],["waiting",this.onContentWaiting],["playing",this.onContentPlaying],["pause",this.onContentPause],["ended",this.onContentEnded],["error",this.onContentError],["externalplaybackchange",this.externalPlayback.onPlayerChange]];return this.metadataCueTracker&&e.push(["timedmetadata",this.onTimedMetadata]),e}async startSession(e){return this.sessionStarter.startSession(e)}play(){return this.adPlayerController?.isAdPlaying()?(this.log("play() suppressed — ad break is active"),Promise.resolve()):this.player.play()}pause(){this.adPlayerController?.isAdPlaying()?this.log("pause() suppressed — ad break is active"):this.player.pause()}seek(e){if(!this.state.sessionActive)return;const t=this.breakScheduler?.getActiveBreak();t?.controls?.snapback?this.log("seek() suppressed — break has snapback enabled"):(this.breakScheduler?.notifyViewerSeek(),this.player.seek(e))}get muted(){return this.adPlayerController?this.adPlayerController.muted:this.player.muted}set muted(e){this.adPlayerController?this.adPlayerController.muted=e:this.player.muted=e}get volume(){return this.adPlayerController?this.adPlayerController.volume:this.player.volume}set volume(e){this.adPlayerController?this.adPlayerController.volume=e:this.player.volume=e}skipAd(){const e=this.adBreakStatusTracker.activeBreak;if(!this.state.sessionActive||!this.adPlayerController?.isAdPlaying()||!e)return this.log("skipAd() suppressed — no active ad break"),!1;const t=e.controls?.skipOffset;if(null==t)return this.log(`skipAd() suppressed — break '${e.id}' declares no controls.skipOffset`),this.diagnose("DA-AD-SKIP-SUPPRESSED","Skip requested but break declares no skipOffset",{breakId:e.id}),!1;const s=this.adBreakStatusTracker.activeBreakElapsedSec;return s<t?(this.log(`skipAd() suppressed — skipOffset ${t}s not reached (${s}s)`),this.diagnose("DA-AD-SKIP-SUPPRESSED","Skip requested before skipOffset",{breakId:e.id,skipOffset:t,elapsedSec:s}),!1):(this.log(`skipAd() — skipping break '${e.id}' at ${s}s (skipOffset ${t}s)`),this.diagnose("DA-AD-SKIPPED","Break skipped by viewer",{breakId:e.id,skipOffset:t,elapsedSec:s}),this.cmcdAdReporter.onSkipRequested(e.id),this.adPlayerController.skipBreak(),!0)}clickAd(){const e=this.adBreakStatusTracker.activeBreak,t=this.adPlayerController?.currentAdAsset()??null;if(!(this.state.sessionActive&&this.adPlayerController?.isAdPlaying()&&e&&t))return this.log("clickAd() suppressed — no ad on screen"),null;const s=t.interaction?.clickThrough;return this.diagnose("DA-AD-CLICKED","Ad clicked",{breakId:e.id,assetId:t.id,clickThrough:s??""}),this.emit("adclick",{type:"adclick",timestamp:Date.now(),break:e,format:this.effectiveFormatOf(e),asset:t,...s?{clickThrough:s}:{}}),s??null}endSession(){this.state.sessionActive&&this.endSessionInternal()}updateAssetParameters(e){if(!this.state.sessionActive)return void console.warn("[OptiViewAds] updateAssetParameters called but no active session.");const t="ssai"===this.mode||this.state.steeredSsaiActive;(t?this.state.ssaiController:this.adPlayerController).updateAssetParameters(e),this.log("Asset parameters updated"+(t?" (SSAI)":""))}updateAssetParameterMacros(e){if(!this.state.sessionActive)return void console.warn("[OptiViewAds] updateAssetParameterMacros called but no active session.");const t="ssai"===this.mode||this.state.steeredSsaiActive;(t?this.state.ssaiController:this.adPlayerController).updateAssetParameterMacros(e),this.log("Asset parameter macros updated"+(t?" (SSAI)":""))}getExternalVendorConfiguration(e){return j(this.state,this.externalVendorConfigurationTracker.entries,this.mode,this.adPlayerController,e)}endSessionInternal(e="end-session"){this.liveStartPushback.stop(),this.externalPlayback.setManaged(!1),this.ssbSwitch?.reset(),this.state.sessionActive=!1,this.state.sessionGeneration++,this.breakLifecycle?.reportAbandonedBreak(e),"ssai"===this.mode?this.state.ssaiController?.stop():(this.state.steeredSsaiActive&&(this.state.ssaiController?.stop(),delete this.state.ssaiController,this.state.steeredSsaiActive=!1),this.manifestService?.reset(),this.breakScheduler?.stop(),this.breakScheduler?.destroy(),this.adBreakStatusTracker.resetAdBreakStatus(),this.state.pauseAdTickInterval&&(clearInterval(this.state.pauseAdTickInterval),this.state.pauseAdTickInterval=null),this.sessionStarter?.clearHeldImmediatePreRollArm(),this.pauseAdController?.reset(),this.breakChainRecorder?.flush(),this.breakTransitionTimer?.reset(),this.adPlayerController?.stopActiveBreak(),this.adPlayerController?.releaseContentHold(),this.adPlayerController?.endGamSession()),this.state.clearSessionIdentity(),this.externalVendorConfigurationTracker.reset(),this.anvatoObservedLogged.clear(),this.seenManifestBreakIds.clear(),this.cmcdAdReporter.onSessionEnd(),this.lensReporter.endSession(),this.diagnose("DA-SESSION-ENDED","Session ended"),this.log("Session ended")}addEventListener(e,t){this.eventHandlers.has(e)||this.eventHandlers.set(e,new Set),this.eventHandlers.get(e).add(t)}removeEventListener(e,t){const s=this.eventHandlers.get(e);s&&s.delete(t)}emit(e,t){V(this.diagnosticBuffer,t),this.cmcdAdReporter.onEvent(t);const s=this.eventHandlers.get(e);if(s)for(const e of s)try{e(t)}catch(e){this.log("Event handler error:",e)}}onDiagnostic(e){this.diagnosticHandlers.add(e)}offDiagnostic(e){this.diagnosticHandlers.delete(e)}exportDiagnostics(){return W(this.state,this.diagnosticBuffer.getAll())}diagnose(e,t,s){if(this.destroyed)return;const r=H[e],i={ts:Date.now(),level:r?.level??"info",code:e,category:r?.category??"unknown",message:t,...s?{context:s}:{}};this.diagnosticBuffer.record(i);for(const e of this.diagnosticHandlers)try{e(i)}catch(e){this.log("Diagnostic handler error:",e)}this.debug&&F("[OptiViewAds]",e,t,s??"")}isAdPlaying(){return this.adPlayerController?.isAdPlaying()??!1}isPictureInPicture(){return this.adPlayerController?.isPictureInPicture()??!1}getPresentationState(){return{pictureInPicture:this.isPictureInPicture(),showing:this.adPlayerController?.isShowingAd()?"ad":"content",insertion:this.adPlayerController?.currentInsertionMode()??"overlay",windowOwner:this.adPlayerController?.pictureInPictureOwner()??null}}effectiveFormatOf(e){const t=this.adPlayerController?.declaredFormatOf(e)??R(e);return w(t,this.isPictureInPicture())}isSessionActive(){return this.state.sessionActive}getAdBreakStatus(){return this.adBreakStatusTracker.getAdBreakStatus()}destroy(){this.contentErrorReloadGuard.dispose(),this.externalPlayback.dispose(),this.ssbSwitch?.dispose();for(const[e,t]of this.contentListeners())this.player.off(e,t);this.state.sessionActive&&this.endSessionInternal("destroy"),this.manifestService?.destroy(),this.pauseAdController?.destroy(),this.adPlayerController?.destroy(),this.state.ssaiController?.stop(),this.lensReporter.destroy(),this.eventHandlers.clear(),this.diagnosticHandlers.clear(),this.destroyed=!0,this.log("Destroyed")}log(...e){this.debug&&F("[OptiViewAds]",...e)}}
@@ -3,14 +3,27 @@ import type { AssetParameterMacros } from './services/assetParameterMacros';
3
3
  import type { ExternalVendorConfiguration } from './types/ExternalVendorConfiguration';
4
4
  import type { OptiViewAdsEventHandler } from './types/Events';
5
5
  import type { DiagnosticHandler, DiagnosticReport } from './diagnostics/DiagnosticEvent';
6
+ /** Configuration for reading manifest external-vendor parameters without a player. @category Configuration */
6
7
  export interface OptiViewAdsExternalVendorsConfig {
8
+ /** URL of the signed manifest to fetch. */
7
9
  manifestUrl: string;
10
+ /** Parameters merged beneath the entry's manifest values. */
8
11
  assetParameters?: Record<string, string>;
12
+ /** Named macros that are resolved when configuration is read. */
9
13
  assetParameterMacros?: AssetParameterMacros;
14
+ /** Optional request interceptor for manifest fetches. */
10
15
  interceptManifestRequest?: ManifestRequestInterceptor;
16
+ /** Optional response interceptor for manifest fetches. */
11
17
  interceptManifestResponse?: ManifestResponseInterceptor;
18
+ /** Enables SDK debug diagnostics. */
12
19
  debug?: boolean;
13
20
  }
21
+ /**
22
+ * Reads the manifest's `external` vendor configuration entries without a player or an ad
23
+ * session, so the application can pass the resolved parameters to another ad provider.
24
+ * Call {@link start}, read entries with {@link getExternalVendorConfiguration}, and listen for
25
+ * `externalvendorconfigurationchange` to pick up template changes while the manifest is polled.
26
+ * @category SDK & Sessions */
14
27
  export declare class OptiViewAdsExternalVendors {
15
28
  private readonly manifestUrl;
16
29
  private readonly manifestService;
@@ -26,19 +39,38 @@ export declare class OptiViewAdsExternalVendors {
26
39
  private startGeneration;
27
40
  private lastValidation;
28
41
  private configSummary;
42
+ /**
43
+ * Create a reader for the manifest at `config.manifestUrl`; nothing is fetched until {@link start}.
44
+ * @throws Error when `manifestUrl` is blank.
45
+ * @category Lifecycle */
29
46
  constructor(config: OptiViewAdsExternalVendorsConfig);
47
+ /** Fetch the initial manifest and begin polling when configured. @category Lifecycle */
30
48
  start(): Promise<void>;
31
49
  private startInternal;
50
+ /** Stop polling and clear the current entries. @category Lifecycle */
32
51
  stop(): void;
52
+ /** Stop polling, clear entries, and release listeners. @category Lifecycle */
33
53
  destroy(): void;
54
+ /** Return all resolved configurations in manifest order. @category Lifecycle */
34
55
  getExternalVendorConfiguration(): ExternalVendorConfiguration[];
56
+ /** Return one resolved configuration using an exact provider match. @category Lifecycle */
35
57
  getExternalVendorConfiguration(provider: string): ExternalVendorConfiguration | undefined;
58
+ /** Replace application parameters used to resolve each configuration. @category Lifecycle */
36
59
  updateAssetParameters(parameters: Record<string, string>): void;
60
+ /**
61
+ * Merge per-name macro updates used to resolve each configuration; `null` marks a name empty
62
+ * (parameters that use it are omitted) and `undefined` removes it.
63
+ * @category Lifecycle */
37
64
  updateAssetParameterMacros(macros: AssetParameterMacroUpdates): void;
65
+ /** Subscribe to external-vendor configuration changes. @category Events */
38
66
  addEventListener(event: 'externalvendorconfigurationchange', handler: OptiViewAdsEventHandler<'externalvendorconfigurationchange'>): void;
67
+ /** Remove a configuration-change listener. @category Events */
39
68
  removeEventListener(event: 'externalvendorconfigurationchange', handler: OptiViewAdsEventHandler<'externalvendorconfigurationchange'>): void;
69
+ /** Subscribe to SDK diagnostics. @category Diagnostics */
40
70
  onDiagnostic(handler: DiagnosticHandler): void;
71
+ /** Remove a diagnostics listener. @category Diagnostics */
41
72
  offDiagnostic(handler: DiagnosticHandler): void;
73
+ /** Export the standalone diagnostic report. @category Diagnostics */
42
74
  exportDiagnostics(): DiagnosticReport;
43
75
  private emit;
44
76
  private diagnoseFetchFailure;
@@ -0,0 +1,4 @@
1
+ export { observeExternalPlayback, setExternalPlaybackAllowed } from './observeExternalPlayback';
2
+ export type { ExternalPlaybackObserver } from './observeExternalPlayback';
3
+ export { createReconnectHold, WEBKIT_RECONNECT_GRACE_MS, WEBKIT_RECONNECT_WINDOW_MS, } from './reconnectHold';
4
+ export type { ReconnectHold } from './reconnectHold';
@@ -0,0 +1 @@
1
+ export{observeExternalPlayback,setExternalPlaybackAllowed}from"./observeExternalPlayback";export{createReconnectHold,WEBKIT_RECONNECT_GRACE_MS,WEBKIT_RECONNECT_WINDOW_MS}from"./reconnectHold";
@@ -0,0 +1,19 @@
1
+ import type { ExternalPlaybackChange, ExternalPlaybackState } from '../interfaces/PlayerAdapter';
2
+ /** Handle returned by {@link observeExternalPlayback}. */
3
+ export interface ExternalPlaybackObserver {
4
+ /** Latest observed state. */
5
+ readonly state: ExternalPlaybackState;
6
+ /** Stop observing. */
7
+ dispose(): void;
8
+ }
9
+ /**
10
+ * Observe the element's external playback state. Prefers the Remote Playback API
11
+ * (`video.remote`), falling back to the WebKit AirPlay signal. Returns `null` when the element
12
+ * exposes neither. Only state changes are emitted. On WebKit, the wireless flag decides `active`,
13
+ * and a `disconnect` is held for a grace period (longer once `connecting` follows) because Safari
14
+ * drops and re-establishes the route by itself on a source change (`disconnect`, `connecting`,
15
+ * `connect`, 0.4-2.6 s), which must not read as AirPlay ending.
16
+ */
17
+ export declare function observeExternalPlayback(video: HTMLVideoElement, emit: (change: ExternalPlaybackChange) => void): ExternalPlaybackObserver | null;
18
+ /** Allow or block external playback of the element (`disableRemotePlayback`, which also gates AirPlay in Safari). */
19
+ export declare function setExternalPlaybackAllowed(video: HTMLVideoElement, allowed: boolean): void;
@@ -0,0 +1 @@
1
+ import{createReconnectHold as e}from"./reconnectHold";const n={connecting:"connecting",connect:"active",disconnect:"inactive"},t={connecting:"connecting",connected:"active",disconnected:"inactive"},c="webkitcurrentplaybacktargetiswirelesschanged";export function observeExternalPlayback(i,o){const r=i.remote,s="webkitCurrentPlaybackTargetIsWireless"in i;if(!r&&!s)return null;const a=s?"airplay":"unknown",l=()=>!0===i.webkitCurrentPlaybackTargetIsWireless;let d=s?l()?"active":r?t[r.state]:"inactive":t[r.state];const v=e=>{e!==d&&(d=e,o({state:d,target:a}))},g=e=>({get state(){return d},dispose:e});if(!s){const e=Object.keys(n).map(e=>({type:e,listener:()=>v(n[e])}));for(const{type:n,listener:t}of e)r.addEventListener(n,t);return g(()=>{for(const{type:n,listener:t}of e)r.removeEventListener(n,t)})}const f=e(()=>v("inactive")),b=()=>{f.cancel(),v("active")},u=()=>{if(f.holding&&f.sawConnecting)return f.cancel(),void v("inactive");f.holding||"inactive"===d||f.begin()},k=()=>l()?b():u();i.addEventListener(c,k);const p=[["connecting",()=>{l()||(f.holding?f.extend():v("connecting"))}],["connect",b],["disconnect",u]];if(r)for(const[e,n]of p)r.addEventListener(e,n);return g(()=>{if(f.dispose(),i.removeEventListener(c,k),r)for(const[e,n]of p)r.removeEventListener(e,n)})}export function setExternalPlaybackAllowed(e,n){e.disableRemotePlayback=!n}
@@ -0,0 +1,19 @@
1
+ /** How long a drop is held before it reads as the external playback ending. */
2
+ export declare const WEBKIT_RECONNECT_GRACE_MS = 500;
3
+ /** How long the hold extends once the platform starts reconnecting (`connecting` after the drop). */
4
+ export declare const WEBKIT_RECONNECT_WINDOW_MS = 5000;
5
+ /** A drop hold: a grace period that a reconnect hint extends to a longer window. */
6
+ export interface ReconnectHold {
7
+ readonly holding: boolean;
8
+ readonly sawConnecting: boolean;
9
+ /** Start the grace hold; no-op while already holding. */
10
+ begin(): void;
11
+ /** Extend to the reconnect window and remember the connecting hint; no-op when not holding. */
12
+ extend(): void;
13
+ /** Drop the hold without firing. */
14
+ cancel(): void;
15
+ /** `cancel()` for teardown. */
16
+ dispose(): void;
17
+ }
18
+ /** Create a {@link ReconnectHold} that calls `onExpire` when the hold runs out. */
19
+ export declare function createReconnectHold(onExpire: () => void): ReconnectHold;
@@ -0,0 +1 @@
1
+ export const WEBKIT_RECONNECT_GRACE_MS=500;export const WEBKIT_RECONNECT_WINDOW_MS=5e3;export function createReconnectHold(e){let n=!1,t=!1,o=null;const c=()=>{null!==o&&clearTimeout(o),o=null},l=()=>{c(),n=!1,t=!1},r=n=>{c(),o=setTimeout(()=>{l(),e()},n)};return{get holding(){return n},get sawConnecting(){return t},begin(){n||(n=!0,t=!1,r(500))},extend(){n&&(t=!0,r(5e3))},cancel:l,dispose:l}}
@@ -49,6 +49,11 @@ export declare const DIAGNOSTIC_CODES: {
49
49
  readonly level: "error";
50
50
  readonly summary: "The break-manifest response failed detached-JWS signature verification (missing/malformed X-Manifest-Signature, unsupported alg, unknown kid, unknown critical param, or signature mismatch); the manifest was rejected and no breaks were scheduled. context: url, reason.";
51
51
  };
52
+ readonly "DA-MANIFEST-EVENT-INVALID": {
53
+ readonly category: "manifest";
54
+ readonly level: "warn";
55
+ readonly summary: "A channel event was ignored because its time window or configuration is invalid, or because events are unsupported for the manifest timebase. context: eventId and index when available.";
56
+ };
52
57
  readonly "DA-PDT-MISSING": {
53
58
  readonly category: "playback";
54
59
  readonly level: "warn";
@@ -132,12 +137,17 @@ export declare const DIAGNOSTIC_CODES: {
132
137
  readonly "DA-BREAK-TRANSITION": {
133
138
  readonly category: "break";
134
139
  readonly level: "info";
135
- readonly summary: "Measured duration of a playback transition into an ad break, between two ads, between chained breaks, or back to content.";
140
+ readonly summary: "Measured duration of a playback transition into an ad break, between two ads, between chained breaks, or back to content. A break-to-break switch runs from the outgoing ad's end to the incoming ad's first frame and adds path (queued, late-queued or cold) and, when the incoming ad was buffered, leadMs. On web an ad-to-ad switch adds path (queued or cold) and leadMs too.";
141
+ };
142
+ readonly "DA-BREAK-CHAIN": {
143
+ readonly category: "chaining";
144
+ readonly level: "info";
145
+ readonly summary: "Summary of one chain of back-to-back breaks, emitted once when content plays again (or the chain is flushed): chainId, per break its id, scheduledStart, plannedDurationSec, beganAtMs, endedAtMs, playedSec, announcedDuringChain and path, per switch its gapMs and path, contentResumeMs and totalMs.";
136
146
  };
137
147
  readonly "DA-AD-SURFACE-REVEALED": {
138
148
  readonly category: "break";
139
149
  readonly level: "info";
140
- readonly summary: "The ad surface became visible: how long it was held back waiting for the ad's first frame, and whether it was revealed because that frame arrived or because the wait timed out.";
150
+ readonly summary: "The ad surface became visible: how long it was held back waiting for the ad's first frame, and whether it was revealed because that frame arrived or because the wait timed out. On web, an ad shown before its first frame reports reason shown-before-frame with visibleGapMs, the time on screen without a picture.";
141
151
  };
142
152
  readonly "DA-BREAK-CUT-LATE": {
143
153
  readonly category: "break";
@@ -229,6 +239,11 @@ export declare const DIAGNOSTIC_CODES: {
229
239
  readonly level: "error";
230
240
  readonly summary: "The GAM/IMA session failed to initialize; GAM breaks will not serve.";
231
241
  };
242
+ readonly "DA-GAM-SESSION-PRELOADED": {
243
+ readonly category: "gam";
244
+ readonly level: "info";
245
+ readonly summary: "A GAM/IMA session was preloaded for the next event; context includes the event boundary and GAM pod identity.";
246
+ };
232
247
  readonly "DA-GAM-START-DEFERRED": {
233
248
  readonly category: "gam";
234
249
  readonly level: "info";
@@ -279,6 +294,31 @@ export declare const DIAGNOSTIC_CODES: {
279
294
  readonly level: "error";
280
295
  readonly summary: "The content player reported a playback error.";
281
296
  };
297
+ readonly "DA-EXTERNAL-PLAYBACK-CHANGED": {
298
+ readonly category: "playback";
299
+ readonly level: "info";
300
+ readonly summary: "The content player's external (AirPlay) playback state changed; context carries the new state and the target.";
301
+ };
302
+ readonly "DA-EXTERNAL-PLAYBACK-BLOCKED": {
303
+ readonly category: "playback";
304
+ readonly level: "warn";
305
+ readonly summary: "AirPlay was requested while an ad break is active or about to start; the content player stays on the device and the request is retried when the break ends.";
306
+ };
307
+ readonly "DA-SSB-SWITCH-FAILED": {
308
+ readonly category: "playback";
309
+ readonly level: "warn";
310
+ readonly summary: "Switching the content player to the SSB stream for AirPlay failed (context.reason: no-restore-source, load-error, load-timeout, external-not-active or receiver-paused); the SDK restores the previous content.";
311
+ };
312
+ readonly "DA-SSB-RESTORE-FAILED": {
313
+ readonly category: "playback";
314
+ readonly level: "error";
315
+ readonly summary: "The previous content source could not be reloaded after the SSB stream (AirPlay ended or the switch failed); the session stays in ssb delivery until the app reloads its content.";
316
+ };
317
+ readonly "DA-EXTERNAL-PLAYBACK-ALLOWED": {
318
+ readonly category: "playback";
319
+ readonly level: "info";
320
+ readonly summary: "An AirPlay request passed the ad-break guard; context.trigger says whether it was the request itself or the retry after a break.";
321
+ };
282
322
  readonly "DA-CONTENT-RESUME-STALLED": {
283
323
  readonly category: "playback";
284
324
  readonly level: "warn";
@@ -342,7 +382,7 @@ export declare const DIAGNOSTIC_CODES: {
342
382
  readonly "DA-DELIVERY-MODE-RESOLVED": {
343
383
  readonly category: "session";
344
384
  readonly level: "info";
345
- readonly summary: "Delivery mode (sgai/ssai) resolved for the session at start and again on an ads-channel switch from the manifest's delivery rules (or the explicit client config), with the detected device profile.";
385
+ readonly summary: "Delivery mode (sgai/ssai/ssb) resolved for the session at start and again on an ads-channel switch from the manifest's delivery rules (or the explicit client config), with the detected device profile; context.source is external-playback or external-playback-ended when AirPlay switched the session to the SSB stream and back.";
346
386
  };
347
387
  readonly "DA-TARGETING-VALUE-UNKNOWN": {
348
388
  readonly category: "manifest";
@@ -417,7 +457,7 @@ export declare const DIAGNOSTIC_CODES: {
417
457
  readonly "DA-ASSET-PARAMETER-OMITTED": {
418
458
  readonly category: "break";
419
459
  readonly level: "info";
420
- readonly summary: "An asset parameter value contained a customer macro that is empty (value `null`), so the parameter was not sent; for GAM `cust_params` only the `&`-separated pair containing the macro was dropped. Raised once per parameter key and macro (per dropped pair for GAM `cust_params`). context: key, macro, pair (GAM `cust_params` only), breakId, assetId.";
460
+ readonly summary: "An asset parameter value contained a customer macro that is empty (value `null`), so the parameter was not sent; for GAM `cust_params`, and for a static-asset value with a literal `&`, only the `&`-separated pair containing the macro was dropped. Raised once per parameter key and macro (per dropped pair when the value is split into pairs). context: key, macro, pair (split values only), breakId, assetId.";
421
461
  };
422
462
  readonly "DA-VAST-SSAI-UNSUPPORTED": {
423
463
  readonly category: "session";
@@ -517,12 +557,12 @@ export declare const DIAGNOSTIC_CODES: {
517
557
  readonly "DA-LIVE-START-PUSHBACK": {
518
558
  readonly category: "playback";
519
559
  readonly level: "info";
520
- readonly summary: "Initial live playback requested a backward seek to the configured target-duration distance. Context includes enabledBy (config or ptsSource), targetDurationSec, targetDurationSource, initialOffsetSec, initialOffsetTargetDurations, requestedOffsetSec, requestedOffsetTargetDurations, targetOffsetSec, targetOffsetTargetDurations, rewindSec, rewindTargetDurations, target and windowClamped. This is a request, not proof of the landing.";
560
+ readonly summary: "Initial live playback requested a backward seek to the configured target-duration distance. Context includes enabledBy (always config), targetDurationSec, targetDurationSource, initialOffsetSec, initialOffsetTargetDurations, requestedOffsetSec, requestedOffsetTargetDurations, targetOffsetSec, targetOffsetTargetDurations, rewindSec, rewindTargetDurations, target and windowClamped. This is a request, not proof of the landing.";
521
561
  };
522
562
  readonly "DA-LIVE-START-PUSHBACK-SKIPPED": {
523
563
  readonly category: "playback";
524
564
  readonly level: "info";
525
- readonly summary: "Initial live playback was left unchanged. The reason identifies a disabled adjustment (enabledBy says whether ptsSource or an explicit enabled decided it), an already sufficient offset, missing target duration or seekable range, invalid configuration, or no safe backward target. Available target-duration and offset measurements are included.";
565
+ readonly summary: "Initial live playback was left unchanged. The reason identifies a disabled adjustment (liveStartPushback.enabled is not true; enabledBy is always config), an already sufficient offset, missing target duration or seekable range, invalid configuration, or no safe backward target. Available target-duration and offset measurements are included.";
526
566
  };
527
567
  readonly "DA-LIVE-START-PUSHBACK-LANDED": {
528
568
  readonly category: "playback";