@dolby-optiview/ads-sdk-core 1.4.0 → 1.6.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 (119) hide show
  1. package/dist/OptiViewAds.d.ts +5 -0
  2. package/dist/OptiViewAds.js +1 -1
  3. package/dist/diagnostics/dispatchDiagnostic.d.ts +4 -0
  4. package/dist/diagnostics/dispatchDiagnostic.js +1 -0
  5. package/dist/generated/diagnosticCodes.d.ts +118 -3
  6. package/dist/generated/diagnosticCodes.js +1 -1
  7. package/dist/generated/version.d.ts +1 -1
  8. package/dist/generated/version.js +1 -1
  9. package/dist/index.d.ts +11 -4
  10. package/dist/index.js +1 -1
  11. package/dist/interfaces/PlayerAdapter.d.ts +27 -0
  12. package/dist/server/index.d.ts +2 -2
  13. package/dist/server/index.js +1 -1
  14. package/dist/services/AdBreakSequencer.d.ts +67 -3
  15. package/dist/services/AdBreakSequencer.js +1 -1
  16. package/dist/services/AdPlayerController.d.ts +9 -4
  17. package/dist/services/AdPlayerController.js +1 -1
  18. package/dist/services/BookmarkStart.d.ts +12 -0
  19. package/dist/services/BookmarkStart.js +1 -0
  20. package/dist/services/BreakScheduler.d.ts +15 -50
  21. package/dist/services/BreakScheduler.js +1 -1
  22. package/dist/services/BreakSchedulerTypes.d.ts +11 -1
  23. package/dist/services/BreakWarningTracker.d.ts +6 -0
  24. package/dist/services/BreakWarningTracker.js +1 -0
  25. package/dist/services/ContentEndTracker.d.ts +17 -0
  26. package/dist/services/ContentEndTracker.js +1 -0
  27. package/dist/services/ExternalPlaybackGate.d.ts +2 -2
  28. package/dist/services/ManifestService.d.ts +1 -1
  29. package/dist/services/ManifestService.js +1 -1
  30. package/dist/services/PauseAdController.d.ts +9 -5
  31. package/dist/services/PauseAdController.js +1 -1
  32. package/dist/services/PlaybackCrossing.d.ts +4 -0
  33. package/dist/services/PlaybackCrossing.js +1 -0
  34. package/dist/services/RewindRearm.js +1 -1
  35. package/dist/services/SchedulerSnapback.d.ts +33 -0
  36. package/dist/services/SchedulerSnapback.js +1 -0
  37. package/dist/services/SlateClock.d.ts +22 -0
  38. package/dist/services/SlateClock.js +1 -0
  39. package/dist/services/SnapbackGuard.d.ts +70 -0
  40. package/dist/services/SnapbackGuard.js +1 -0
  41. package/dist/services/VastAdManager.d.ts +2 -0
  42. package/dist/services/VastAdManager.js +1 -1
  43. package/dist/services/adPlayer/AdPlayerCallbacks.d.ts +11 -1
  44. package/dist/services/adPlayer/AdPlayerEvents.d.ts +21 -1
  45. package/dist/services/adPlayer/AdPlayerEvents.js +1 -1
  46. package/dist/services/adPlayer/AdPlayerState.d.ts +14 -0
  47. package/dist/services/adPlayer/AdPlayerState.js +1 -1
  48. package/dist/services/adPlayer/AdPreloader.d.ts +8 -1
  49. package/dist/services/adPlayer/AdPreloader.js +1 -1
  50. package/dist/services/adPlayer/AdStage.js +1 -1
  51. package/dist/services/adPlayer/AssetPlayback.d.ts +1 -0
  52. package/dist/services/adPlayer/AssetPlayback.js +1 -1
  53. package/dist/services/adPlayer/BreakSequenceDriver.d.ts +7 -1
  54. package/dist/services/adPlayer/BreakSequenceDriver.js +1 -1
  55. package/dist/services/adPlayer/ContentResumeCoordinator.d.ts +6 -0
  56. package/dist/services/adPlayer/ContentResumeCoordinator.js +1 -1
  57. package/dist/services/adPlayer/PauseAdPresenter.d.ts +10 -9
  58. package/dist/services/adPlayer/PauseAdPresenter.js +1 -1
  59. package/dist/services/adPlayer/PauseAdTracking.d.ts +13 -0
  60. package/dist/services/adPlayer/PauseAdTracking.js +1 -0
  61. package/dist/services/adPlayer/SharedElementBreakPlayer.d.ts +31 -2
  62. package/dist/services/adPlayer/SharedElementBreakPlayer.js +1 -1
  63. package/dist/services/adPlayer/SlateCoordinator.d.ts +58 -0
  64. package/dist/services/adPlayer/SlateCoordinator.js +1 -0
  65. package/dist/services/adPlayer/SlatePlayback.d.ts +89 -0
  66. package/dist/services/adPlayer/SlatePlayback.js +1 -0
  67. package/dist/services/adPlayer/VariantSelector.d.ts +10 -1
  68. package/dist/services/adPlayer/VariantSelector.js +1 -1
  69. package/dist/services/adPlayer/connectPauseAd.js +1 -1
  70. package/dist/services/adPlayer/presentation/PresentationLayout.d.ts +5 -2
  71. package/dist/services/adPlayer/presentation/PresentationLayout.js +1 -1
  72. package/dist/services/breakTiming.d.ts +1 -0
  73. package/dist/services/breakTiming.js +1 -1
  74. package/dist/services/checkEventBreak.d.ts +5 -0
  75. package/dist/services/checkEventBreak.js +1 -0
  76. package/dist/services/parseBreakManifest.d.ts +2 -1
  77. package/dist/services/parseBreakManifest.js +1 -1
  78. package/dist/services/parseVastPause.d.ts +18 -0
  79. package/dist/services/parseVastPause.js +1 -0
  80. package/dist/services/resolveLshapeAnchor.d.ts +25 -0
  81. package/dist/services/resolveLshapeAnchor.js +1 -0
  82. package/dist/services/resolveResumePoint.d.ts +3 -1
  83. package/dist/services/resolveResumePoint.js +1 -1
  84. package/dist/services/resolveSlate.d.ts +25 -0
  85. package/dist/services/resolveSlate.js +1 -0
  86. package/dist/services/resolveSlateFill.d.ts +39 -0
  87. package/dist/services/resolveSlateFill.js +1 -0
  88. package/dist/services/ssbSwitchSignals.d.ts +3 -0
  89. package/dist/services/ssbSwitchSignals.js +1 -1
  90. package/dist/session/AdBreakStatusTracker.d.ts +22 -2
  91. package/dist/session/AdBreakStatusTracker.js +1 -1
  92. package/dist/session/BreakLifecycle.js +1 -1
  93. package/dist/session/ContentPlaybackStart.d.ts +23 -0
  94. package/dist/session/ContentPlaybackStart.js +1 -0
  95. package/dist/session/DiscoveredManifestUrl.d.ts +44 -0
  96. package/dist/session/DiscoveredManifestUrl.js +1 -0
  97. package/dist/session/ScheduledBreaksTracker.d.ts +22 -0
  98. package/dist/session/ScheduledBreaksTracker.js +1 -0
  99. package/dist/session/SessionStarter.d.ts +3 -9
  100. package/dist/session/SessionStarter.js +1 -1
  101. package/dist/session/SessionStarterDeps.d.ts +3 -0
  102. package/dist/session/SessionStarterHelpers.d.ts +22 -1
  103. package/dist/session/SessionStarterHelpers.js +1 -1
  104. package/dist/session/SessionState.d.ts +16 -0
  105. package/dist/session/SessionState.js +1 -1
  106. package/dist/session/SsbSwitch.js +1 -1
  107. package/dist/session/buildScheduledBreaks.d.ts +9 -0
  108. package/dist/session/buildScheduledBreaks.js +1 -0
  109. package/dist/session/contentSnapback.d.ts +5 -1
  110. package/dist/session/contentSnapback.js +1 -1
  111. package/dist/session/scheduledBreaksSession.d.ts +6 -0
  112. package/dist/session/scheduledBreaksSession.js +1 -0
  113. package/dist/types/BreakManifest.d.ts +40 -2
  114. package/dist/types/BreakManifest.js +1 -1
  115. package/dist/types/Events.d.ts +13 -4
  116. package/dist/types/OptiViewAdsConfig.d.ts +15 -2
  117. package/dist/types/ScheduledBreak.d.ts +17 -0
  118. package/dist/types/ScheduledBreak.js +1 -0
  119. package/package.json +1 -1
@@ -1 +1 @@
1
- import{Timebase as e}from"../types/BreakManifest";import{resolveExternalVendorConfigurations as n}from"../services/resolveExternalVendorConfigurations";import{detectDeviceProfile as t}from"../services/detectDeviceType";import{isGamVendorParameters as s}from"../services/GamPodUrlBuilder";import{decideDeliveryMode as i,detectChannelSwitch as o,gamPodIdentityChanged as r}from"../services/channelSwitch";import{nextEventBoundaryMs as a,resolveEventManifest as d}from"../services/resolveChannelEvents";import{filterSsaiClientBreaks as l}from"../services/resolveDeliveryMode";import{gamPodIdentityMismatches as u,resolveGamVendorConfiguration as f}from"../services/resolveGamVendorConfiguration";export const EVENT_SESSION_PRELOAD_LEAD_MS=8e3;export function isSsaiSessionConfig(e){return"stitcherUrl"in e}export function isNonBlankString(e){return"string"==typeof e&&""!==e.trim()}export function resolveEventAtPosition(n,t,s){return n.timebase!==e.WALLCLOCK||!1===s.isLive?{manifest:n,eventId:n.eventId??null}:d(n,t)}export function rejectGamPodPreloadForBoundaryIdentity(n,t,i,o){const r=n.pendingManifestUpdate??n.sessionManifest;if(!r||r.timebase!==e.WALLCLOCK||!1===i.isLive||"string"!=typeof o.start)return"none";const a=Date.parse(o.start);if(!Number.isFinite(a))return"none";const l=d(r,a).manifest,c=f(l).identity;if(!c)return"none";const v=n.effectiveSessionManifest??r,m=f(v).identity;if(m&&c.networkCode===m.networkCode&&c.customAssetKey===m.customAssetKey)return"none";const p=(Array.isArray(o.variant)?o.variant:[o.variant]).flatMap(e=>e.assets).filter(e=>"vendor"===e.type&&"gam"===e.vendor&&s(e.vendorParameters));if(0===p.length)return"none";const y=p.find(e=>0===u(c,e.vendorParameters).length);if(y)return"defer";const g=p[0];if(!g)return"none";for(const e of u(c,g.vendorParameters))t.diagnose("DA-GAM-POD-IDENTITY-MISMATCH",`GAM asset ${g.id} skipped: vendorParameters.${e.field} "${e.assetValue}" does not match the stream session's "${e.sessionValue}".`,{assetId:g.id,field:e.field,assetValue:e.assetValue,sessionValue:e.sessionValue});return"mismatch"}export function diagnoseIgnoredDeliveryModeChange(e,n,s){if(!e.sessionDeliveryMode)return;const o="ssb"===e.sessionDeliveryMode?e.ssb?.previousMode:e.sessionDeliveryMode;if(!o)return;const r=i(s,t(),null,"manifest");r.manifestMode!==o&&n.diagnose("DA-DELIVERY-MODE-CHANGE-IGNORED",`Polled manifest resolves delivery mode "${r.manifestMode}" but "${o}" is pinned for this session.`,{polledMode:r.manifestMode,pinnedMode:o})}export function resolveSessionDeliveryMode(e,n,s,o="manifest"){const r=e.sessionDeliveryMode,a=i(s,t(),n.hasExplicitMode?n.mode:null,o);null!==a.unknownMode&&n.diagnose("DA-DELIVERY-MODE-UNKNOWN",`Delivery rule ${a.ruleIndex} carries unrecognized mode "${a.unknownMode}"; using sgai.`,{unknownMode:a.unknownMode,ruleIndex:a.ruleIndex});for(const e of a.targetingIssues)n.diagnose(e.code,`Targeting ${e.field} was rejected.`,{field:e.field,...void 0===e.value?{}:{value:e.value}});return n.hasExplicitMode&&a.manifestMode!==a.mode&&n.diagnose("DA-DELIVERY-MODE-OVERRIDDEN",`Manifest delivery rules resolve "${a.manifestMode}" but the explicit config mode "${a.mode}" takes precedence.`,{manifestMode:a.manifestMode,configuredMode:a.mode}),e.sessionDeliveryMode=a.mode,n.diagnose("DA-DELIVERY-MODE-RESOLVED",`Delivery mode: ${a.mode}`,{mode:a.mode,ruleIndex:a.ruleIndex,source:a.source,deviceProfile:t(),..."manifest"!==o?{previousMode:r}:{}}),a.mode}export function isStaleSessionStart(e,n,t){return t!==e.sessionGeneration&&(n.log("Session start aborted — the session ended or was replaced while starting"),!0)}export function emitChannelChange(e,n){e.emit("adchannelchange",{type:"adchannelchange",timestamp:Date.now(),...n})}export function adoptExternalVendorConfigurations(e,n){e.externalVendorConfigurationTracker.adopt(n)}export function diagnoseExternalVendorConfigurations(e,t){const s=n(t);s.invalidCount>0&&e.diagnose("DA-EXTERNAL-VENDOR-INVALID",`Ignored ${s.invalidCount} invalid external vendorConfiguration `+(1===s.invalidCount?"entry":"entries")+".",{count:s.invalidCount}),s.duplicateProviders.length>0&&e.diagnose("DA-EXTERNAL-VENDOR-DUPLICATE",`Ignored duplicate external vendorConfiguration entries for providers: ${s.duplicateProviders.join(", ")}.`,{providers:s.duplicateProviders})}export function handleEventBoundaryTick(n,s,d,u){const c=n.sessionManifest;if(!n.sessionActive||!c||c.timebase!==e.WALLCLOCK||!1===s.player.isLive||null===d||!Number.isFinite(d))return!0;const v=n.pendingManifestUpdate,m=v??c,p=resolveEventAtPosition(m,d,s.player),y=p.eventId!==n.activeEventId;if(y){if(n.channelSwitchInFlight)return!1;if(s.breakScheduler.getActiveBreak())return!0}if(!y)return v&&(n.pendingManifestUpdate=null,n.effectiveSessionManifest=p.manifest,u.applyManifestUpdate(p.manifest)),function(e,n,s,o){const d=a(s,o),l=null===d?null:d-o;if(null===d||null===l||l<0||l>8e3||e.steeredSsaiActive)return void n.adPlayerController.gamPodBridge.discardPreparedGamSession();const u=resolveEventAtPosition(s,o,n.player).manifest,c=resolveEventAtPosition(s,d,n.player).manifest,v=n.hasExplicitMode?n.mode:null,m=i(u,t(),v,"event-boundary").mode,p=i(c,t(),v,"event-boundary").mode,y=f(u),g=f(c);if("sgai"!==m||"sgai"!==p||!g.identity||y.identity?.customAssetKey===g.identity.customAssetKey||!r(u,c)||!function(e,n){return e.breaks.some(e=>{const t="string"==typeof e.start?Date.parse(e.start):Number.NaN,s=Array.isArray(e.variant)?e.variant:[e.variant];return Number.isFinite(t)&&t>=n&&t<=n+8e3&&s.some(e=>e.assets.some(e=>"vendor"===e.type&&"gam"===e.vendor))})}(c,d))return void n.adPlayerController.gamPodBridge.discardPreparedGamSession();const M=g.identity,h=`${d}:${M.networkCode}:${M.customAssetKey}`;if(e.eventSessionPreloadKeys.has(h))return;e.eventSessionPreloadKeys.add(h),n.adPlayerController.gamPodBridge.prepareNextGamSession(M,g.sgaiAssetParameters).then(e=>{e&&n.diagnose("DA-GAM-SESSION-PRELOADED","GAM session preloaded for the next event.",{boundaryMs:d,networkCode:M.networkCode,customAssetKey:M.customAssetKey})}).catch(e=>{n.log("Next-event GAM session preload failed",e)})}(n,s,m,d),!0;const g=n.effectiveSessionManifest??c,M=i(p.manifest,t(),s.hasExplicitMode?s.mode:null,"event-boundary").mode,h=o(g,p.manifest)??(M!==n.sessionDeliveryMode?{previousChannelId:g.channelId??null,channelId:p.manifest.channelId??g.channelId??null,reason:"event-boundary"}:null);if(v&&(n.pendingManifestUpdate=null),s.breakScheduler.holdBreakStarts(),n.activeEventId=p.eventId,h)return u.switchChannel(p.manifest,{...h,reason:"event-boundary"},!0,m),!1;n.effectiveSessionManifest=p.manifest;try{!function(e,n,t,s,i,o){const r=e.sessionDeliveryMode??"sgai",a=o.resolveDeliveryMode(s);adoptExternalVendorConfigurations(n,s);const d=f(s);e.steeredSsaiActive?e.ssaiController?.setManifestAssetParameters(d.ssai?.assetParameters):n.adPlayerController.setManifestAssetParameters(d.sgaiAssetParameters);const u=e.steeredSsaiActive?l(s):s;n.breakScheduler.applyEventBoundary(u),n.pauseAdController.updateManifest(u),n.cmcdAdReporter.updateEventId(i);const c=s.channelId??t.channelId??null;emitChannelChange(n,{previousChannelId:t.channelId??c,channelId:c,eventId:i,reason:"event-boundary",manifestUrl:e.sessionConfig.manifestUrl,previousDeliveryMode:r,deliveryMode:a})}(n,s,g,p.manifest,p.eventId,u)}finally{s.breakScheduler.releaseBreakStarts()}return!0}
1
+ import{Timebase as e}from"../types/BreakManifest";import{resolveExternalVendorConfigurations as n}from"../services/resolveExternalVendorConfigurations";import{resolveSlate as t}from"../services/resolveSlate";import{detectDeviceProfile as s}from"../services/detectDeviceType";import{isGamVendorParameters as i}from"../services/GamPodUrlBuilder";import{decideDeliveryMode as r,detectChannelSwitch as o,gamPodIdentityChanged as a}from"../services/channelSwitch";import{nextEventBoundaryMs as d,resolveEventManifest as l}from"../services/resolveChannelEvents";import{filterSsaiClientBreaks as c}from"../services/resolveDeliveryMode";import{gamPodIdentityMismatches as u,resolveGamVendorConfiguration as f}from"../services/resolveGamVendorConfiguration";export const EVENT_SESSION_PRELOAD_LEAD_MS=8e3;export function updateSessionAssetParameters(e,n,t,s,i){if(!e.sessionActive)return void console.warn("[OptiViewAds] updateAssetParameters called but no active session.");const r="ssai"===n||e.steeredSsaiActive;(r?e.ssaiController:t).updateAssetParameters(s),i("Asset parameters updated"+(r?" (SSAI)":""))}export function updateSessionAssetParameterMacros(e,n,t,s,i){if(!e.sessionActive)return void console.warn("[OptiViewAds] updateAssetParameterMacros called but no active session.");const r="ssai"===n||e.steeredSsaiActive;(r?e.ssaiController:t).updateAssetParameterMacros(s),i("Asset parameter macros updated"+(r?" (SSAI)":""))}export function isSsaiSessionConfig(e){return"stitcherUrl"in e}export function isNonBlankString(e){return"string"==typeof e&&""!==e.trim()}export function resolveEventAtPosition(n,t,s){return n.timebase!==e.WALLCLOCK||!1===s.isLive?{manifest:n,eventId:n.eventId??null}:l(n,t)}export function rejectGamPodPreloadForBoundaryIdentity(n,t,s,r){const o=n.pendingManifestUpdate??n.sessionManifest;if(!o||o.timebase!==e.WALLCLOCK||!1===s.isLive||"string"!=typeof r.start)return"none";const a=Date.parse(r.start);if(!Number.isFinite(a))return"none";const d=l(o,a).manifest,c=f(d).identity;if(!c)return"none";const v=n.effectiveSessionManifest??o,p=f(v).identity;if(p&&c.networkCode===p.networkCode&&c.customAssetKey===p.customAssetKey)return"none";const m=(Array.isArray(r.variant)?r.variant:[r.variant]).flatMap(e=>e.assets).filter(e=>"vendor"===e.type&&"gam"===e.vendor&&i(e.vendorParameters));if(0===m.length)return"none";const g=m.find(e=>0===u(c,e.vendorParameters).length);if(g)return"defer";const M=m[0];if(!M)return"none";for(const e of u(c,M.vendorParameters))t.diagnose("DA-GAM-POD-IDENTITY-MISMATCH",`GAM asset ${M.id} skipped: vendorParameters.${e.field} "${e.assetValue}" does not match the stream session's "${e.sessionValue}".`,{assetId:M.id,field:e.field,assetValue:e.assetValue,sessionValue:e.sessionValue});return"mismatch"}export function diagnoseIgnoredDeliveryModeChange(e,n,t){if(!e.sessionDeliveryMode)return;const i="ssb"===e.sessionDeliveryMode?e.ssb?.previousMode:e.sessionDeliveryMode;if(!i)return;const o=r(t,s(),null,"manifest");o.manifestMode!==i&&n.diagnose("DA-DELIVERY-MODE-CHANGE-IGNORED",`Polled manifest resolves delivery mode "${o.manifestMode}" but "${i}" is pinned for this session.`,{polledMode:o.manifestMode,pinnedMode:i})}export function resolveSessionDeliveryMode(e,n,t,i="manifest"){const o=e.sessionDeliveryMode,a=r(t,s(),n.hasExplicitMode?n.mode:null,i);null!==a.unknownMode&&n.diagnose("DA-DELIVERY-MODE-UNKNOWN",`Delivery rule ${a.ruleIndex} carries unrecognized mode "${a.unknownMode}"; using sgai.`,{unknownMode:a.unknownMode,ruleIndex:a.ruleIndex});for(const e of a.targetingIssues)n.diagnose(e.code,`Targeting ${e.field} was rejected.`,{field:e.field,...void 0===e.value?{}:{value:e.value}});return n.hasExplicitMode&&a.manifestMode!==a.mode&&n.diagnose("DA-DELIVERY-MODE-OVERRIDDEN",`Manifest delivery rules resolve "${a.manifestMode}" but the explicit config mode "${a.mode}" takes precedence.`,{manifestMode:a.manifestMode,configuredMode:a.mode}),e.sessionDeliveryMode=a.mode,n.diagnose("DA-DELIVERY-MODE-RESOLVED",`Delivery mode: ${a.mode}`,{mode:a.mode,ruleIndex:a.ruleIndex,source:a.source,deviceProfile:s(),..."manifest"!==i?{previousMode:o}:{}}),a.mode}export function isStaleSessionStart(e,n,t){return t!==e.sessionGeneration&&(n.log("Session start aborted — the session ended or was replaced while starting"),!0)}export function emitChannelChange(e,n){e.emit("adchannelchange",{type:"adchannelchange",timestamp:Date.now(),...n})}export function adoptSlate(e,n){e.adPlayerController?.setSlate(t(n.slate).slate)}export function adoptExternalVendorConfigurations(e,n){e.externalVendorConfigurationTracker.adopt(n)}export function diagnoseExternalVendorConfigurations(e,t){const s=n(t);s.invalidCount>0&&e.diagnose("DA-EXTERNAL-VENDOR-INVALID",`Ignored ${s.invalidCount} invalid external vendorConfiguration `+(1===s.invalidCount?"entry":"entries")+".",{count:s.invalidCount}),s.duplicateProviders.length>0&&e.diagnose("DA-EXTERNAL-VENDOR-DUPLICATE",`Ignored duplicate external vendorConfiguration entries for providers: ${s.duplicateProviders.join(", ")}.`,{providers:s.duplicateProviders})}export function handleEventBoundaryTick(n,t,i,l){const u=n.sessionManifest;if(!n.sessionActive||!u||u.timebase!==e.WALLCLOCK||!1===t.player.isLive||null===i||!Number.isFinite(i))return!0;const v=n.pendingManifestUpdate,p=v??u,m=resolveEventAtPosition(p,i,t.player),g=m.eventId!==n.activeEventId;if(g){if(n.channelSwitchInFlight)return!1;if(t.breakScheduler.getActiveBreak())return!0}if(!g)return v&&(n.pendingManifestUpdate=null,n.effectiveSessionManifest=m.manifest,l.applyManifestUpdate(m.manifest)),function(e,n,t,i){const o=d(t,i),l=null===o?null:o-i;if(null===o||null===l||l<0||l>8e3||e.steeredSsaiActive)return void n.adPlayerController.gamPodBridge.discardPreparedGamSession();const c=resolveEventAtPosition(t,i,n.player).manifest,u=resolveEventAtPosition(t,o,n.player).manifest,v=n.hasExplicitMode?n.mode:null,p=r(c,s(),v,"event-boundary").mode,m=r(u,s(),v,"event-boundary").mode,g=f(c),M=f(u);if("sgai"!==p||"sgai"!==m||!M.identity||g.identity?.customAssetKey===M.identity.customAssetKey||!a(c,u)||!function(e,n){return e.breaks.some(e=>{const t="string"==typeof e.start?Date.parse(e.start):Number.NaN,s=Array.isArray(e.variant)?e.variant:[e.variant];return Number.isFinite(t)&&t>=n&&t<=n+8e3&&s.some(e=>e.assets.some(e=>"vendor"===e.type&&"gam"===e.vendor))})}(u,o))return void n.adPlayerController.gamPodBridge.discardPreparedGamSession();const A=M.identity,h=`${o}:${A.networkCode}:${A.customAssetKey}`;if(e.eventSessionPreloadKeys.has(h))return;e.eventSessionPreloadKeys.add(h),n.adPlayerController.gamPodBridge.prepareNextGamSession(A,M.sgaiAssetParameters).then(e=>{e&&n.diagnose("DA-GAM-SESSION-PRELOADED","GAM session preloaded for the next event.",{boundaryMs:o,networkCode:A.networkCode,customAssetKey:A.customAssetKey})}).catch(e=>{n.log("Next-event GAM session preload failed",e)})}(n,t,p,i),!0;const M=n.effectiveSessionManifest??u,A=r(m.manifest,s(),t.hasExplicitMode?t.mode:null,"event-boundary").mode,h=o(M,m.manifest)??(A!==n.sessionDeliveryMode?{previousChannelId:M.channelId??null,channelId:m.manifest.channelId??M.channelId??null,reason:"event-boundary"}:null);if(v&&(n.pendingManifestUpdate=null),t.breakScheduler.holdBreakStarts(),n.activeEventId=m.eventId,h)return l.switchChannel(m.manifest,{...h,reason:"event-boundary"},!0,p),!1;n.effectiveSessionManifest=m.manifest;try{!function(e,n,t,s,i,r){const o=e.sessionDeliveryMode??"sgai",a=r.resolveDeliveryMode(s);adoptExternalVendorConfigurations(n,s),adoptSlate(n,s);const d=f(s);e.steeredSsaiActive?e.ssaiController?.setManifestAssetParameters(d.ssai?.assetParameters):n.adPlayerController.setManifestAssetParameters(d.sgaiAssetParameters);const l=e.steeredSsaiActive?c(s):s;n.breakScheduler.applyEventBoundary(l),n.pauseAdController.updateManifest(l),n.cmcdAdReporter.updateEventId(i);const u=s.channelId??t.channelId??null;emitChannelChange(n,{previousChannelId:t.channelId??u,channelId:u,eventId:i,reason:"event-boundary",manifestUrl:e.resolvedManifestUrl??e.sessionConfig.manifestUrl??"",previousDeliveryMode:o,deliveryMode:a})}(n,t,M,m.manifest,m.eventId,l)}finally{t.breakScheduler.releaseBreakStarts()}return!0}export function applyManifestUpdate(e,n,t){if(!e.sessionActive)return;adoptExternalVendorConfigurations(n,t),adoptSlate(n,t),diagnoseIgnoredDeliveryModeChange(e,n,t);const s=f(t);e.steeredSsaiActive?e.ssaiController?.setManifestAssetParameters(s.ssai?.assetParameters):n.adPlayerController.setManifestAssetParameters(s.sgaiAssetParameters);const i=e.steeredSsaiActive?c(t):t;n.breakScheduler.updateManifest(i),n.pauseAdController.updateManifest(i)}export function startManifestPolling(e,n,t){n.manifestService.startPolling(s=>{if(!e.sessionActive)return;const i=n.notedManifestBreakIds(s);n.diagnose("DA-MANIFEST-POLLED","Manifest polled",{breaks:s.breaks.length,newBreakIds:i,pollingIntervalSeconds:n.manifestService.currentPollingIntervalSeconds});const r=e.sessionManifest,a=r?o(r,s):null;if(e.pendingChannelSwitch||a&&(n.breakScheduler.getActiveBreak()||e.ssb))return e.pendingChannelSwitch=s,void n.log("Channel switch deferred until the active break ends");if(a)return e.sessionManifest=s,e.pendingManifestUpdate=null,void t.switchChannel(s,a);const d="wallclock"===s.timebase&&!1!==n.player.isLive&&Boolean(s.events?.length||r?.events?.length);e.sessionManifest=s,d?e.pendingManifestUpdate=s:(e.pendingManifestUpdate=null,e.effectiveSessionManifest=s,applyManifestUpdate(e,n,s))},t=>{e.sessionActive&&n.diagnose("DA-MANIFEST-POLL-FAILED",t.message)})}export function applyPendingChannelSwitch(e,n,t){if(!e.sessionActive||!e.pendingChannelSwitch||e.ssb)return;const s=e.pendingChannelSwitch;e.pendingChannelSwitch=null;const i=o(e.sessionManifest,s);i?t.switchChannel(s,i):applyManifestUpdate(e,n,s)}
@@ -50,6 +50,22 @@ export declare class SessionState {
50
50
  heldPreRollWaitStartedAt: number | null;
51
51
  snapbackSeekInFlight: boolean;
52
52
  sessionConfig: SessionConfig | null;
53
+ /**
54
+ * The manifest URL the session actually polls — the explicit `sessionConfig.manifestUrl` or the
55
+ * URL resolved from player discovery. Null between sessions.
56
+ */
57
+ resolvedManifestUrl: string | null;
58
+ /** True when the active session's URL came from player discovery (enables auto-restart). */
59
+ manifestUrlFromDiscovery: boolean;
60
+ /**
61
+ * Bumped by every startSession()/endSession()/destroy() call so a pending discovery wait can
62
+ * tell it was superseded — unlike `sessionGeneration`, it moves even with no active session.
63
+ */
64
+ discoveryWaitGeneration: number;
65
+ /** Wake callbacks for in-flight discovery waits; run on every bump so a superseded wait exits. */
66
+ private discoveryWaiters;
67
+ bumpDiscoveryWaitGeneration(): void;
68
+ addDiscoveryWaiter(wake: () => void): () => void;
53
69
  sessionManifest: BreakManifest | null;
54
70
  effectiveSessionManifest: BreakManifest | null;
55
71
  activeEventId: string | null;
@@ -1 +1 @@
1
- export class SessionState{constructor(){this.sessionActive=!1,this.sessionGeneration=0,this.adStartDelayElapsed=!1,this.sessionDeliveryMode=null,this.ssb=null,this.steeredSsaiActive=!1,this.steeredSsaiOptions=null,this.pauseAdTickInterval=null,this.heldPreRollArmInterval=null,this.manifestOrigin=null,this.stitcherOrigin=null,this.heldImmediatePreRollId=null,this.heldPreRollArmed=!1,this.heldPreRollWaitStartedAt=null,this.snapbackSeekInFlight=!1,this.sessionConfig=null,this.sessionManifest=null,this.effectiveSessionManifest=null,this.activeEventId=null,this.eventSessionPreloadKeys=new Set,this.pendingManifestUpdate=null,this.channelSwitchInFlight=!1,this.pendingChannelSwitch=null}clearSessionIdentity(){this.sessionDeliveryMode=null,this.sessionManifest=null,this.effectiveSessionManifest=null,this.activeEventId=null,this.eventSessionPreloadKeys.clear(),this.pendingManifestUpdate=null,this.channelSwitchInFlight=!1,this.pendingChannelSwitch=null}}
1
+ export class SessionState{constructor(){this.sessionActive=!1,this.sessionGeneration=0,this.adStartDelayElapsed=!1,this.sessionDeliveryMode=null,this.ssb=null,this.steeredSsaiActive=!1,this.steeredSsaiOptions=null,this.pauseAdTickInterval=null,this.heldPreRollArmInterval=null,this.manifestOrigin=null,this.stitcherOrigin=null,this.heldImmediatePreRollId=null,this.heldPreRollArmed=!1,this.heldPreRollWaitStartedAt=null,this.snapbackSeekInFlight=!1,this.sessionConfig=null,this.resolvedManifestUrl=null,this.manifestUrlFromDiscovery=!1,this.discoveryWaitGeneration=0,this.discoveryWaiters=new Set,this.sessionManifest=null,this.effectiveSessionManifest=null,this.activeEventId=null,this.eventSessionPreloadKeys=new Set,this.pendingManifestUpdate=null,this.channelSwitchInFlight=!1,this.pendingChannelSwitch=null}bumpDiscoveryWaitGeneration(){this.discoveryWaitGeneration++;for(const e of[...this.discoveryWaiters])e()}addDiscoveryWaiter(e){return this.discoveryWaiters.add(e),()=>{this.discoveryWaiters.delete(e)}}clearSessionIdentity(){this.sessionDeliveryMode=null,this.resolvedManifestUrl=null,this.manifestUrlFromDiscovery=!1,this.sessionManifest=null,this.effectiveSessionManifest=null,this.activeEventId=null,this.eventSessionPreloadKeys.clear(),this.pendingManifestUpdate=null,this.channelSwitchInFlight=!1,this.pendingChannelSwitch=null}}
@@ -1 +1 @@
1
- import{buildAssetParameterMacroTable as e}from"../services/assetParameterMacroTable";import{resolveSsbStream as s}from"../services/resolveGamVendorConfiguration";import{resolveSsbRestoreChange as t}from"../services/ssbSwitchSignals";import{buildSsbStreamUrl as i,resolveSsbRestorePosition as r}from"../services/ssbStream";export const SSB_ACTIVE_TIMEOUT_MS=1500;export const SSB_LOAD_TIMEOUT_MS=15e3;export const SSB_RECEIVER_PAUSE_WINDOW_MS=3e3;export const SSB_RESTORE_PLAYING_BACKSTOP_MS=1e4;export const SSB_RESTORE_ATTEMPTS=2;export class SsbSwitchController{constructor(t,r,a){this.state=t,this.starter=r,this.deps=a,this.timers=new Set,this.previousPaused=!1,this.streamReady=!1,this.onAllowed=t=>{const{state:r}=this,{player:a}=this.deps,o=r.effectiveSessionManifest??r.sessionManifest,n=o?s(o.vendorConfiguration):null,l=r.sessionConfig;if(!r.sessionActive||r.ssb||!o||!n||!l)return;const c=a.sourceUrl;if("string"!=typeof c||""===c.trim())return void this.deps.diagnose("DA-SSB-SWITCH-FAILED","The content adapter reports no source to restore; AirPlay stays on the device.",{reason:"no-restore-source",trigger:t});const h=this.deps.adPlayerController;this.runtimeParams=h?.updatedAssetParameters,this.sessionMacros=h?.sessionAssetParameterMacros,this.previousPaused=a.paused;const d=r.sessionDeliveryMode??"sgai";r.ssb={previousMode:d,previousSource:c,previousPosition:a.currentTime,previousLive:a.isLive??!Number.isFinite(a.duration),switchedAt:Date.now(),trigger:t,phase:"switching",activeAt:null,announced:!1},h?.stopActiveBreak(),h?.releaseContentHold(),h?.endGamSession(),r.steeredSsaiActive&&(r.ssaiController?.stop(),delete r.ssaiController,r.steeredSsaiActive=!1),this.deps.breakScheduler?.suspend(),this.deps.pauseAdController?.reset(),r.sessionDeliveryMode="ssb",this.deps.diagnose("DA-DELIVERY-MODE-RESOLVED","AirPlay started: playing the SSB stream",{mode:"ssb",source:"external-playback",previousMode:d,trigger:t}),this.rollSession(o);const p=i(n,{manifest:n.assetParameters,session:l.assetParameters,runtime:this.runtimeParams},e({customerMacros:h?.sessionAssetParameterMacros??{},element:null,breakDurationSec:void 0})),u=r.ssb;this.streamReady=!1;const m=()=>r.ssb===u&&"switching"===u.phase,S=()=>{this.streamReady=!0,m()&&("active"!==a.externalPlaybackState?this.later(()=>{m()&&("active"===a.externalPlaybackState?this.markActive(u):this.fail("external-not-active",t,p))},1500):this.markActive(u))};a.load(p).then(()=>(S(),this.previousPaused||r.ssb!==u||"restoring"===u.phase?void 0:a.play())).catch(e=>this.fail("load-error",t,p,e)),a.setExternalPlaybackAllowed?.(!0),this.later(()=>{m()&&!this.streamReady&&this.fail("load-timeout",t,p)},15e3)},this.onExternalPlaybackChange=e=>{const{state:s}=this,t=s.ssb,i=e;t&&("active"===i?.state&&"switching"===t.phase&&this.streamReady?this.markActive(t):"inactive"===i?.state&&"restoring"!==t.phase&&this.restoreFromSsb("external-playback-ended"))},this.onPause=()=>{const e=this.state.ssb;"active"===e?.phase&&null!==e.activeAt&&Date.now()-e.activeAt<=3e3&&this.fail("receiver-paused",e.trigger)},a.player.on("externalplaybackchange",this.onExternalPlaybackChange),a.player.on("pause",this.onPause)}get isActive(){return null!==this.state.ssb}dispose(){this.clearTimers(),this.state.ssb=null,this.deps.player.off("externalplaybackchange",this.onExternalPlaybackChange),this.deps.player.off("pause",this.onPause)}reset(){this.clearTimers(),this.streamReady=!1,this.state.ssb=null}markActive(e){this.clearTimers(),e.phase="active",e.activeAt=Date.now(),this.later(()=>{this.state.ssb===e&&"active"===e.phase&&(e.announced=!0,this.emitChange(e.previousMode,"ssb"),this.deps.adBreakStatusTracker.refreshDeliveryMode())},3e3)}fail(e,s,t,i){this.state.ssb&&"restoring"!==this.state.ssb.phase&&(this.deps.diagnose("DA-SSB-SWITCH-FAILED",`The SSB stream switch failed: ${e}`,{reason:e,uri:t,trigger:s,...void 0===i?{}:{message:i instanceof Error?i.message:String(i)}}),this.restoreFromSsb("switch-failed"))}async restoreFromSsb(e){const{state:s}=this,{player:i}=this.deps,r=s.ssb,a=s.effectiveSessionManifest??s.sessionManifest,o=s.sessionConfig,{starter:n}=this;if(!(r&&"restoring"!==r.phase&&a&&o&&n))return;r.phase="restoring",this.clearTimers(),i.setExternalPlaybackAllowed?.(!1);const l=s.sessionGeneration,c=this.deps.breakScheduler;c?.setContentReloading(!0);let h=!1;if("ssai"===r.previousMode&&(h=await n.switchToSteeredSsai(a,o,!0)),h||(h=await this.restoreSgai(r,a,o,n,l)),s.ssb!==r)return;if(!h)return r.phase="failed",void c?.setContentReloading(!1);if(await this.waitForPlaying(),s.ssb!==r)return;c?.setContentReloading(!1),c?.skipPendingBreaksDueBy("played-by-ssb"),c?.resume();const d="ssai"===r.previousMode&&s.steeredSsaiActive?"ssai":"sgai";s.sessionDeliveryMode=d,s.ssb=null,this.deps.diagnose("DA-DELIVERY-MODE-RESOLVED","AirPlay ended: content restored",{mode:d,source:"external-playback-ended",cause:e}),this.rollSession(a);const p=t(r.announced,d);p&&this.emitChange(p.previousDeliveryMode,p.deliveryMode),this.deps.adBreakStatusTracker.refreshDeliveryMode(),n.applyPendingChannelSwitch()}async restoreSgai(e,s,t,i,a){const{player:o}=this.deps,{state:n}=this;if(!await this.reloadPrevious(e.previousSource))return!1;if(n.ssb!==e)return!0;const l=Number.isFinite(o.duration)?o.duration:null,c=r({live:e.previousLive,recordedPosition:e.previousPosition,duration:l});null!==c&&o.seek(c),this.previousPaused||await o.play().catch(()=>{}),await i.switchToSgai(s,t,a,!0);const h=this.deps.adPlayerController;return this.sessionMacros&&h?.setSessionAssetParameterMacros(this.sessionMacros),this.runtimeParams&&h?.updateAssetParameters(this.runtimeParams),!0}async reloadPrevious(e){let s;for(let t=1;t<=2;t++)try{return await this.deps.player.load(e),!0}catch(e){s=e}return this.deps.diagnose("DA-SSB-RESTORE-FAILED","The previous content source could not be reloaded.",{attempt:2,reason:"load-error",source:e,message:s instanceof Error?s.message:String(s)}),!1}waitForPlaying(){return new Promise(e=>{const s=()=>{this.deps.player.off("playing",s),e()};this.deps.player.on("playing",s),this.later(s,1e4)})}rollSession(e){this.deps.cmcdAdReporter.onSessionEnd(),this.deps.cmcdAdReporter.onSessionStart(e),this.deps.lensReporter.endSession(),this.deps.lensReporter.startSession()}emitChange(e,s){const{state:t}=this,i=t.effectiveSessionManifest??t.sessionManifest,r=i?.channelId??null;this.deps.emit("adchannelchange",{type:"adchannelchange",timestamp:Date.now(),previousChannelId:r,channelId:r,eventId:t.activeEventId,reason:"external-playback",manifestUrl:t.sessionConfig?.manifestUrl??"",previousDeliveryMode:e,deliveryMode:s})}later(e,s){const t=setTimeout(()=>{this.timers.delete(t),e()},s);this.timers.add(t)}clearTimers(){for(const e of this.timers)clearTimeout(e);this.timers.clear()}}
1
+ import{buildAssetParameterMacroTable as e}from"../services/assetParameterMacroTable";import{resolveSsbStream as s}from"../services/resolveGamVendorConfiguration";import{resolveSsbRestoreChange as t,resolveSsbSwitchPreviousPaused as i}from"../services/ssbSwitchSignals";import{buildSsbStreamUrl as a,resolveSsbRestorePosition as r}from"../services/ssbStream";export const SSB_ACTIVE_TIMEOUT_MS=1500;export const SSB_LOAD_TIMEOUT_MS=15e3;export const SSB_RECEIVER_PAUSE_WINDOW_MS=3e3;export const SSB_RESTORE_PLAYING_BACKSTOP_MS=1e4;export const SSB_RESTORE_ATTEMPTS=2;export class SsbSwitchController{constructor(t,r,o){this.state=t,this.starter=r,this.deps=o,this.timers=new Set,this.previousPaused=!1,this.streamReady=!1,this.onAllowed=t=>{const{state:r}=this,{player:o}=this.deps,n=r.effectiveSessionManifest??r.sessionManifest,l=n?s(n.vendorConfiguration):null,d=r.sessionConfig;if(!r.sessionActive||r.ssb||!n||!l||!d)return;const c=o.sourceUrl;if("string"!=typeof c||""===c.trim())return void this.deps.diagnose("DA-SSB-SWITCH-FAILED","The content adapter reports no source to restore; AirPlay stays on the device.",{reason:"no-restore-source",trigger:t});const h=this.deps.adPlayerController;this.runtimeParams=h?.updatedAssetParameters,this.sessionMacros=h?.sessionAssetParameterMacros,this.previousPaused=i(t,o.paused);const p=r.sessionDeliveryMode??"sgai";r.ssb={previousMode:p,previousSource:c,previousPosition:o.currentTime,previousLive:o.isLive??!Number.isFinite(o.duration),switchedAt:Date.now(),trigger:t,phase:"switching",activeAt:null,announced:!1},h?.stopActiveBreak(),h?.abandonContentResume(),h?.releaseContentHold(),h?.endGamSession(),r.steeredSsaiActive&&(r.ssaiController?.stop(),delete r.ssaiController,r.steeredSsaiActive=!1),this.deps.breakScheduler?.suspend(),this.deps.pauseAdController?.reset(),r.sessionDeliveryMode="ssb",this.deps.diagnose("DA-DELIVERY-MODE-RESOLVED","AirPlay started: playing the SSB stream",{mode:"ssb",source:"external-playback",previousMode:p,trigger:t}),this.rollSession(n);const u=a(l,{manifest:l.assetParameters,session:d.assetParameters,runtime:this.runtimeParams},e({customerMacros:h?.sessionAssetParameterMacros??{},element:null,breakDurationSec:void 0})),m=r.ssb;this.streamReady=!1;const v=()=>r.ssb===m&&"switching"===m.phase,S=()=>{this.streamReady=!0,v()&&("active"!==o.externalPlaybackState?this.later(()=>{v()&&("active"===o.externalPlaybackState?this.markActive(m):this.fail("external-not-active",t,u))},1500):this.markActive(m))};o.load(u).then(()=>(S(),this.previousPaused||r.ssb!==m||"restoring"===m.phase?void 0:o.play())).catch(e=>this.fail("load-error",t,u,e)),o.setExternalPlaybackAllowed?.(!0),this.later(()=>{v()&&!this.streamReady&&this.fail("load-timeout",t,u)},15e3)},this.onExternalPlaybackChange=e=>{const{state:s}=this,t=s.ssb,i=e;t&&("active"===i?.state&&"switching"===t.phase&&this.streamReady?this.markActive(t):"inactive"===i?.state&&"restoring"!==t.phase&&this.restoreFromSsb("external-playback-ended"))},this.onPause=()=>{const e=this.state.ssb;"active"===e?.phase&&null!==e.activeAt&&Date.now()-e.activeAt<=3e3&&this.fail("receiver-paused",e.trigger)},o.player.on("externalplaybackchange",this.onExternalPlaybackChange),o.player.on("pause",this.onPause)}get isActive(){return null!==this.state.ssb}dispose(){this.clearTimers(),this.state.ssb=null,this.deps.player.off("externalplaybackchange",this.onExternalPlaybackChange),this.deps.player.off("pause",this.onPause)}reset(){this.clearTimers(),this.streamReady=!1,this.state.ssb=null}markActive(e){this.clearTimers(),e.phase="active",e.activeAt=Date.now(),this.later(()=>{this.state.ssb===e&&"active"===e.phase&&(e.announced=!0,this.emitChange(e.previousMode,"ssb"),this.deps.adBreakStatusTracker.refreshDeliveryMode())},3e3)}fail(e,s,t,i){this.state.ssb&&"restoring"!==this.state.ssb.phase&&(this.deps.diagnose("DA-SSB-SWITCH-FAILED",`The SSB stream switch failed: ${e}`,{reason:e,uri:t,trigger:s,...void 0===i?{}:{message:i instanceof Error?i.message:String(i)}}),this.restoreFromSsb("switch-failed"))}async restoreFromSsb(e){const{state:s}=this,{player:i}=this.deps,a=s.ssb,r=s.effectiveSessionManifest??s.sessionManifest,o=s.sessionConfig,{starter:n}=this;if(!(a&&"restoring"!==a.phase&&r&&o&&n))return;a.phase="restoring",this.clearTimers(),i.setExternalPlaybackAllowed?.(!1);const l=s.sessionGeneration,d=this.deps.breakScheduler;d?.setContentReloading(!0);let c=!1;if("ssai"===a.previousMode&&(c=await n.switchToSteeredSsai(r,o,!0)),c||(c=await this.restoreSgai(a,r,o,n,l)),s.ssb!==a)return;if(!c)return a.phase="failed",void d?.setContentReloading(!1);if(await this.waitForPlaying(),s.ssb!==a)return;d?.setContentReloading(!1),d?.skipPendingBreaksDueBy("played-by-ssb"),d?.resume();const h="ssai"===a.previousMode&&s.steeredSsaiActive?"ssai":"sgai";s.sessionDeliveryMode=h,s.ssb=null,this.deps.diagnose("DA-DELIVERY-MODE-RESOLVED","AirPlay ended: content restored",{mode:h,source:"external-playback-ended",cause:e}),this.rollSession(r);const p=t(a.announced,h);p&&this.emitChange(p.previousDeliveryMode,p.deliveryMode),this.deps.adBreakStatusTracker.refreshDeliveryMode(),n.applyPendingChannelSwitch()}async restoreSgai(e,s,t,i,a){const{player:o}=this.deps,{state:n}=this;if(!await this.reloadPrevious(e.previousSource))return!1;if(n.ssb!==e)return!0;const l=Number.isFinite(o.duration)?o.duration:null,d=r({live:e.previousLive,recordedPosition:e.previousPosition,duration:l});null!==d&&o.seek(d),this.previousPaused||await o.play().catch(()=>{}),await i.switchToSgai(s,t,a,!0);const c=this.deps.adPlayerController;return this.sessionMacros&&c?.setSessionAssetParameterMacros(this.sessionMacros),this.runtimeParams&&c?.updateAssetParameters(this.runtimeParams),!0}async reloadPrevious(e){let s;for(let t=1;t<=2;t++)try{return await this.deps.player.load(e),!0}catch(e){s=e}return this.deps.diagnose("DA-SSB-RESTORE-FAILED","The previous content source could not be reloaded.",{attempt:2,reason:"load-error",source:e,message:s instanceof Error?s.message:String(s)}),!1}waitForPlaying(){return new Promise(e=>{const s=()=>{this.deps.player.off("playing",s),e()};this.deps.player.on("playing",s),this.later(s,1e4)})}rollSession(e){this.deps.cmcdAdReporter.onSessionEnd(),this.deps.cmcdAdReporter.onSessionStart(e),this.deps.lensReporter.endSession(),this.deps.lensReporter.startSession()}emitChange(e,s){const{state:t}=this,i=t.effectiveSessionManifest??t.sessionManifest,a=i?.channelId??null;this.deps.emit("adchannelchange",{type:"adchannelchange",timestamp:Date.now(),previousChannelId:a,channelId:a,eventId:t.activeEventId,reason:"external-playback",manifestUrl:t.resolvedManifestUrl??t.sessionConfig?.manifestUrl??"",previousDeliveryMode:e,deliveryMode:s})}later(e,s){const t=setTimeout(()=>{this.timers.delete(t),e()},s);this.timers.add(t)}clearTimers(){for(const e of this.timers)clearTimeout(e);this.timers.clear()}}
@@ -0,0 +1,9 @@
1
+ import { type Break, type BreakManifest } from '../types/BreakManifest';
2
+ import type { ScheduledBreak } from '../types/ScheduledBreak';
3
+ export interface ScheduledBreakInputs {
4
+ canPlay?: (brk: Break) => boolean;
5
+ mapPts?: (pts: number) => number | null;
6
+ played?: ReadonlySet<string>;
7
+ excluded?: ReadonlySet<string>;
8
+ }
9
+ export declare function buildScheduledBreaks(manifest: BreakManifest | null, inputs?: ScheduledBreakInputs): ScheduledBreak[];
@@ -0,0 +1 @@
1
+ import{BreakEvent as t,Timebase as e,getBreakEvent as i}from"../types/BreakManifest";import{breakPlanEmptyReason as s}from"../services/AdBreakSequencer";export function buildScheduledBreaks(r,n={}){if(!r)return[];const o=[];for(const a of r.breaks){const d=i(a);if(null!==d&&d!==t.START&&d!==t.END)continue;const p=n.canPlay??(t=>null===s(t,"overlay"));if(n.excluded?.has(a.id)||!p(a))continue;const l=d===t.START?"pre":d===t.END?"post":"mid";let u="pre"===l?0:null,c=null;if("mid"===l)if(r.timebase===e.WALLCLOCK&&"string"==typeof a.start){const t=new Date(a.start);Number.isFinite(t.getTime())&&(c=t.toISOString())}else"number"==typeof a.start&&(u=r.timebase===e.MEDIATIME?a.start:n.mapPts?.(a.start)??null);o.push({id:a.id,position:l,startSec:u,startDate:c,durationSec:a.duration,played:n.played?.has(a.id)??!1})}const a={pre:0,mid:1,post:2},d=t=>t.startSec??(Date.parse(t.startDate??"")||0);return o.sort((t,e)=>a[t.position]-a[e.position]||("mid"===t.position?d(t)-d(e):0)||(t.id<e.id?-1:t.id>e.id?1:0))}
@@ -1,12 +1,16 @@
1
1
  import type { PlayerAdapter } from '../interfaces/PlayerAdapter';
2
- import type { Break } from '../types/BreakManifest';
2
+ import { type Break, type BreakFormat } from '../types/BreakManifest';
3
3
  import type { SessionState } from './SessionState';
4
+ import type { BreakScheduler } from '../services/BreakScheduler';
5
+ /** VOD snapback is unavailable on TV browsers, including React Native web TV shells. */
6
+ export declare function configureVodSnapback(scheduler: BreakScheduler, player: PlayerAdapter, canPlay: (brk: Break) => boolean, selectFormat?: (brk: Break) => BreakFormat | undefined): void;
4
7
  export interface SnapbackDeps {
5
8
  player: PlayerAdapter;
6
9
  state: Pick<SessionState, 'snapbackSeekInFlight'>;
7
10
  activeBreak(): Break | null | undefined;
8
11
  /** A viewer seek (not our own corrective one) landed. */
9
12
  onViewerSeek(): void;
13
+ onSeeked?(): void;
10
14
  log(...args: unknown[]): void;
11
15
  }
12
16
  /** The content `seeked` handler: snaps a viewer seek back to the break start while the break locks it. */
@@ -1 +1 @@
1
- export function createSnapbackHandler(e){return()=>{if(e.state.snapbackSeekInFlight)return void(e.state.snapbackSeekInFlight=!1);e.onViewerSeek();const t=e.activeBreak();if(!t?.controls?.snapback)return;const a="number"==typeof t.start?t.start:e.player.currentTime;Math.abs(e.player.currentTime-a)<=.5||(e.log("Snapback: seek detected during locked break, snapping back to break start"),e.state.snapbackSeekInFlight=!0,e.player.seek(a))}}
1
+ import{DeviceType as e}from"../types/BreakManifest";import{detectDeviceProfile as t}from"../services/detectDeviceType";export function configureVodSnapback(a,n,r,c){a.setSnapbackOnSeek(t().deviceType!==e.TV,r,c),a.setOnSnapback(e=>n.seek(e.target))}export function createSnapbackHandler(e){return()=>{if(e.state.snapbackSeekInFlight)return void(e.state.snapbackSeekInFlight=!1);e.onViewerSeek(),e.onSeeked?.();const t=e.activeBreak();if(!t?.controls?.snapback)return;const a="number"==typeof t.start?t.start:e.player.currentTime;Math.abs(e.player.currentTime-a)<=.5||(e.log("Snapback: seek detected during locked break, snapping back to break start"),e.state.snapbackSeekInFlight=!0,e.player.seek(a))}}
@@ -0,0 +1,6 @@
1
+ import type { AdPlayerController } from '../services/AdPlayerController';
2
+ import type { MetadataCueTracker } from '../services/MetadataCueTracker';
3
+ import type { OptiViewAdsEventMap, OptiViewAdsEventType } from '../types/Events';
4
+ import { ScheduledBreaksTracker } from './ScheduledBreaksTracker';
5
+ export declare function createScheduledBreaksTracker(controller: () => AdPlayerController | undefined, cues: () => MetadataCueTracker | null, emit: (event: 'scheduledbreakschange', data: OptiViewAdsEventMap['scheduledbreakschange']) => void): ScheduledBreaksTracker;
6
+ export declare function noteScheduledBreakEvent(tracker: ScheduledBreaksTracker, event: OptiViewAdsEventMap[OptiViewAdsEventType]): void;
@@ -0,0 +1 @@
1
+ import{ScheduledBreaksTracker as e}from"./ScheduledBreaksTracker";export function createScheduledBreaksTracker(a,r,d){return new e(e=>d("scheduledbreakschange",{type:"scheduledbreakschange",timestamp:Date.now(),breaks:e}),{canPlay:e=>null===a()?.breakNothingToPlayReason(e),mapPts:e=>r()?.mediaTimeForPts(e)??null})}export function noteScheduledBreakEvent(e,a){"adbreakbegin"===a.type&&e.markPlayed(a.break.id),"adbreakskipped"===a.type&&"overlap"===a.reason&&e.exclude(a.break.id)}
@@ -1,4 +1,4 @@
1
- /** Break Manifest types, per https://github.com/Dolby-OptiView/optiview-ads-manifest-spec/blob/v1.9.0/spec.md. */
1
+ /** Break Manifest types, per https://github.com/Dolby-OptiView/optiview-ads-manifest-spec/blob/v1.12.0/spec.md. */
2
2
  /** Which 'time' reference break start times use. @category Break Manifests & Ad Formats */
3
3
  export declare enum Timebase {
4
4
  /** An absolute ISO-8601 timestamp */
@@ -111,9 +111,26 @@ export interface BreakManifest {
111
111
  vendorConfiguration?: VendorConfiguration[];
112
112
  /** Event-specific configuration windows on a wallclock live stream. */
113
113
  events?: ChannelEvent[];
114
+ /** Media shown during the part of a break no ad covers (spec 1.12.0); absent when the channel has no slate. */
115
+ slate?: Slate;
114
116
  /** The available breaks */
115
117
  breaks: Break[];
116
118
  }
119
+ /**
120
+ * Media that replaces the ad during the part of a break no ad covers, until the break ends
121
+ * (manifest spec 1.12.0). Video loops from its start; an image stays visible.
122
+ * @category Break Manifests & Ad Formats
123
+ */
124
+ export interface Slate {
125
+ /**
126
+ * Seconds (>= 0) of the break that must remain for the slate to start; `0` starts it for any
127
+ * remaining time.
128
+ * @default 5
129
+ */
130
+ minDuration?: number;
131
+ /** Static assets without `duration`; the SDK plays the first, also when it cannot play it. */
132
+ assets: StaticAsset[];
133
+ }
117
134
  /**
118
135
  * An event window on a wallclock live stream and the session configuration in effect while it
119
136
  * runs. Crossing its start or end emits `adchannelchange` with reason `event-boundary`.
@@ -291,21 +308,42 @@ export interface OverlaySize {
291
308
  width: number;
292
309
  height: number;
293
310
  }
294
- /** Double-box variant — ad on one side, companion on the other. Content pauses. @category Break Manifests & Ad Formats */
311
+ /** Double-box variant — ad alongside continuing content, with a companion backdrop. @category Break Manifests & Ad Formats */
295
312
  export interface DoubleBreakVariant extends BaseBreakVariant {
296
313
  format: BreakFormat.DOUBLE;
297
314
  /** Paired ad + companion assets. */
298
315
  assets: AssetWithCompanion[];
299
316
  }
317
+ /**
318
+ * The corner of the picture area the L-shape video box is anchored to (manifest spec 1.11.0).
319
+ * The box is 65% of the picture, inset 5% from the two edges of its corner.
320
+ * @category Break Manifests & Ad Formats
321
+ */
322
+ export declare enum LshapeAnchor {
323
+ TOP_LEFT = "top_left",
324
+ TOP_RIGHT = "top_right",
325
+ BOTTOM_LEFT = "bottom_left",
326
+ BOTTOM_RIGHT = "bottom_right"
327
+ }
300
328
  /** L-shape (ad) variant — content shrinks to pip, ad fills main area, companion fills the backdrop. Content pauses. @category Break Manifests & Ad Formats */
301
329
  export interface LshapeAdBreakVariant extends BaseBreakVariant {
302
330
  format: BreakFormat.LSHAPE_AD;
331
+ /**
332
+ * The corner the video box is anchored to. An unrecognised value renders `top_left`.
333
+ * @default LshapeAnchor.TOP_LEFT
334
+ */
335
+ anchor?: LshapeAnchor;
303
336
  /** Paired ad + companion assets. */
304
337
  assets: AssetWithCompanion[];
305
338
  }
306
339
  /** L-shape (content) variant — content shrinks to pip and keeps playing, ad fills the main area. @category Break Manifests & Ad Formats */
307
340
  export interface LshapeContentBreakVariant extends BaseBreakVariant {
308
341
  format: BreakFormat.LSHAPE_CONTENT;
342
+ /**
343
+ * The corner the video box is anchored to. An unrecognised value renders `top_left`.
344
+ * @default LshapeAnchor.TOP_LEFT
345
+ */
346
+ anchor?: LshapeAnchor;
309
347
  /** Ad assets (no companion). */
310
348
  assets: Asset[];
311
349
  }
@@ -1 +1 @@
1
- export var Timebase;!function(e){e.WALLCLOCK="wallclock",e.PTS="pts",e.MEDIATIME="mediatime"}(Timebase||(Timebase={}));export function isGamVendorConfiguration(e){return"gam"===e.type}export var DeliveryMode;!function(e){e.SGAI="sgai",e.SSAI="ssai"}(DeliveryMode||(DeliveryMode={}));export var BreakEvent;!function(e){e.START="start",e.PAUSE="pause",e.END="end"}(BreakEvent||(BreakEvent={}));export var BreakFormat;!function(e){e.SINGLE="single",e.DOUBLE="double",e.LSHAPE_AD="lshape_ad",e.LSHAPE_CONTENT="lshape_content",e.OVERLAY="overlay"}(BreakFormat||(BreakFormat={}));export var DeviceType;!function(e){e.CONSOLE="console",e.DESKTOP="desktop",e.MOBILE="mobile",e.TABLET="tablet",e.TV="tv"}(DeviceType||(DeviceType={}));export var Platform;!function(e){e.WEB="web",e.ANDROID="android",e.IOS="ios",e.TVOS="tvos",e.ROKU="roku"}(Platform||(Platform={}));export var Os;!function(e){e.ANDROID="android",e.CHROMEOS="chromeos",e.IOS="ios",e.IPADOS="ipados",e.LINUX="linux",e.MACOS="macos",e.ROKUOS="rokuos",e.TIZEN="tizen",e.TVOS="tvos",e.VIZIO="vizio",e.WEBOS="webos",e.WINDOWS="windows",e.XBOX="xbox"}(Os||(Os={}));export var AssetType;!function(e){e.STATIC="static",e.VAST="vast",e.VENDOR="vendor"}(AssetType||(AssetType={}));export var MediaType;!function(e){e.VIDEO="video",e.IMAGE="image"}(MediaType||(MediaType={}));export var Vendor;!function(e){e.GAM="gam"}(Vendor||(Vendor={}));export function isEventTrigger(e){return"object"==typeof e&&null!==e&&"event"===e.type}export function getBreakEvent(e){return"pre"===e.position?BreakEvent.START:isEventTrigger(e.start)?e.start.event:null}export function isPauseBreak(e){return getBreakEvent(e)===BreakEvent.PAUSE}export function getPreRollDelay(e){return getBreakEvent(e)!==BreakEvent.START?null:"pre"===e.position?e.delay??0:isEventTrigger(e.start)?e.start.delay??0:0}export function isPostRollBreak(e){return getBreakEvent(e)===BreakEvent.END}export function getPostRollDelay(e){return isEventTrigger(e.start)&&e.start.event===BreakEvent.END?e.start.delay??0:null}export function isStaticAsset(e){return e.type===AssetType.STATIC}export function isVastAsset(e){return e.type===AssetType.VAST}export function isVendorAsset(e){return e.type===AssetType.VENDOR}
1
+ export var Timebase;!function(e){e.WALLCLOCK="wallclock",e.PTS="pts",e.MEDIATIME="mediatime"}(Timebase||(Timebase={}));export function isGamVendorConfiguration(e){return"gam"===e.type}export var DeliveryMode;!function(e){e.SGAI="sgai",e.SSAI="ssai"}(DeliveryMode||(DeliveryMode={}));export var BreakEvent;!function(e){e.START="start",e.PAUSE="pause",e.END="end"}(BreakEvent||(BreakEvent={}));export var BreakFormat;!function(e){e.SINGLE="single",e.DOUBLE="double",e.LSHAPE_AD="lshape_ad",e.LSHAPE_CONTENT="lshape_content",e.OVERLAY="overlay"}(BreakFormat||(BreakFormat={}));export var DeviceType;!function(e){e.CONSOLE="console",e.DESKTOP="desktop",e.MOBILE="mobile",e.TABLET="tablet",e.TV="tv"}(DeviceType||(DeviceType={}));export var Platform;!function(e){e.WEB="web",e.ANDROID="android",e.IOS="ios",e.TVOS="tvos",e.ROKU="roku"}(Platform||(Platform={}));export var Os;!function(e){e.ANDROID="android",e.CHROMEOS="chromeos",e.IOS="ios",e.IPADOS="ipados",e.LINUX="linux",e.MACOS="macos",e.ROKUOS="rokuos",e.TIZEN="tizen",e.TVOS="tvos",e.VIZIO="vizio",e.WEBOS="webos",e.WINDOWS="windows",e.XBOX="xbox"}(Os||(Os={}));export var LshapeAnchor;!function(e){e.TOP_LEFT="top_left",e.TOP_RIGHT="top_right",e.BOTTOM_LEFT="bottom_left",e.BOTTOM_RIGHT="bottom_right"}(LshapeAnchor||(LshapeAnchor={}));export var AssetType;!function(e){e.STATIC="static",e.VAST="vast",e.VENDOR="vendor"}(AssetType||(AssetType={}));export var MediaType;!function(e){e.VIDEO="video",e.IMAGE="image"}(MediaType||(MediaType={}));export var Vendor;!function(e){e.GAM="gam"}(Vendor||(Vendor={}));export function isEventTrigger(e){return"object"==typeof e&&null!==e&&"event"===e.type}export function getBreakEvent(e){return"pre"===e.position?BreakEvent.START:isEventTrigger(e.start)?e.start.event:null}export function isPauseBreak(e){return getBreakEvent(e)===BreakEvent.PAUSE}export function getPreRollDelay(e){return getBreakEvent(e)!==BreakEvent.START?null:"pre"===e.position?e.delay??0:isEventTrigger(e.start)?e.start.delay??0:0}export function isPostRollBreak(e){return getBreakEvent(e)===BreakEvent.END}export function getPostRollDelay(e){return isEventTrigger(e.start)&&e.start.event===BreakEvent.END?e.start.delay??0:null}export function isStaticAsset(e){return e.type===AssetType.STATIC}export function isVastAsset(e){return e.type===AssetType.VAST}export function isVendorAsset(e){return e.type===AssetType.VENDOR}
@@ -1,10 +1,18 @@
1
1
  import { Break, Asset, BreakFormat } from './BreakManifest';
2
2
  import type { SessionDeliveryMode } from './OptiViewAdsConfig';
3
+ import type { ScheduledBreak } from './ScheduledBreak';
4
+ /** Viewer-specific schedule changed by value. @category Events */
5
+ export interface ScheduledBreaksChangeEvent extends OptiViewAdsEvent {
6
+ type: 'scheduledbreakschange';
7
+ /** Independent snapshot of the current session schedule. */
8
+ breaks: ScheduledBreak[];
9
+ }
3
10
  /**
4
11
  * Lifecycle phase reported by {@link AdBreakStatus}.
5
12
  * - `idle`: no break is upcoming or active.
6
13
  * - `upcoming`: a break is scheduled in the future (including pre-break warnings).
7
14
  * - `active`: a break is currently playing.
15
+ * - `complete`: an active break ended, including cuts/errors; emitted once before `idle` and `adbreakend`.
8
16
  * @category Events */
9
17
  export type AdBreakPhase = 'idle' | 'upcoming' | 'active' | 'complete';
10
18
  /** Why a scheduled ad break was refused before it began (`adbreakskipped` event). @category Events */
@@ -17,7 +25,7 @@ export interface AdBreakSkippedEvent extends OptiViewAdsEvent {
17
25
  }
18
26
  /** Playback-gated snapshot of an ad break for UI countdowns (`adbreakstatus` event, `sdk.getAdBreakStatus()`). @category Events */
19
27
  export interface AdBreakStatus {
20
- /** Current lifecycle phase of the ad break. */
28
+ /** Current lifecycle phase. `complete` is transient; polling after cleanup returns `idle`. */
21
29
  phase: AdBreakPhase;
22
30
  /** The break this status describes; `null` when `phase` is `idle`. */
23
31
  break: Break | null;
@@ -25,13 +33,13 @@ export interface AdBreakStatus {
25
33
  format?: BreakFormat | undefined;
26
34
  /** Seconds until the break starts; for a pre-break warning, the countdown value when it fired. */
27
35
  secondsUntilBreak?: number | undefined;
28
- /** Estimated seconds left in the active break; `undefined` when not yet derivable (e.g. a backdrop break with no per-ad `timeupdate`). */
36
+ /** Estimated seconds left in the active break; zero on `complete`, not a measure of ads watched. `undefined` when not yet derivable (e.g. a backdrop break with no per-ad `timeupdate`). */
29
37
  breakRemainingSec?: number | undefined;
30
38
  /** 0-based index of the ad currently playing within the break. */
31
39
  adIndex?: number | undefined;
32
40
  /** Total number of ads in the break, when known. */
33
41
  totalAds?: number | undefined;
34
- /** Number of ads remaining including the currently playing one; after an ad ends, the ads still to come. */
42
+ /** Number of ads remaining including the currently playing one; after an ad ends, the ads still to come. Zero on `complete`, including cuts/errors. */
35
43
  adsRemaining?: number | undefined;
36
44
  /** Whether the countdown is advancing (ad rendered, playback progressing). `false` while buffering or before a held pre-roll's first frame. */
37
45
  ticking: boolean;
@@ -47,7 +55,7 @@ export interface AdBreakStatus {
47
55
  deliveryMode?: SessionDeliveryMode | undefined;
48
56
  }
49
57
  /** Event types emitted by the OptiViewAds SDK. @category Events */
50
- export type OptiViewAdsEventType = 'adbreakbegin' | 'adbreakend' | 'adbreakskipped' | 'adbegin' | 'adend' | 'aderror' | 'adfirstquartile' | 'admidpoint' | 'adthirdquartile' | 'adtimeupdate' | 'adbreakstatus' | 'adclick' | 'waiting' | 'playing' | 'volumechange' | 'adchannelchange' | 'externalvendorconfigurationchange';
58
+ export type OptiViewAdsEventType = 'scheduledbreakschange' | 'adbreakbegin' | 'adbreakend' | 'adbreakskipped' | 'adbegin' | 'adend' | 'aderror' | 'adfirstquartile' | 'admidpoint' | 'adthirdquartile' | 'adtimeupdate' | 'adbreakstatus' | 'adclick' | 'waiting' | 'playing' | 'volumechange' | 'adchannelchange' | 'externalvendorconfigurationchange';
51
59
  /** Which player a playback event originated from. @category Events */
52
60
  export type PlaybackSource = 'ad' | 'content';
53
61
  /** Base event interface. @category Events */
@@ -249,6 +257,7 @@ export interface ExternalVendorConfigurationChangeEvent extends OptiViewAdsEvent
249
257
  }
250
258
  /** Union type of all SDK events. @category Events */
251
259
  export type OptiViewAdsEventMap = {
260
+ scheduledbreakschange: ScheduledBreaksChangeEvent;
252
261
  adbreakbegin: AdBreakBeginEvent;
253
262
  adbreakend: AdBreakEndEvent;
254
263
  adbreakskipped: AdBreakSkippedEvent;
@@ -357,6 +357,13 @@ export interface OptiViewAdsConfig {
357
357
  * @default false
358
358
  */
359
359
  continueContentDuringBreak?: boolean;
360
+ /**
361
+ * Let `startSession()` run without `manifestUrl` by using the ads manifest URL the content
362
+ * player discovers (THEOplayer with a THEOlive source on web and Android). Off by default:
363
+ * `startSession()` then requires `manifestUrl`, and adapters do not observe discovery.
364
+ * @default false
365
+ */
366
+ manifestUrlDiscovery?: boolean;
360
367
  /**
361
368
  * AirPlay (external playback) guard, active only when the manifest carries a server-side
362
369
  * beaconing (`ssb`) stream and the content adapter can gate external playback.
@@ -408,8 +415,14 @@ export interface SsaiConfig {
408
415
  }
409
416
  /** Per-session configuration passed to startSession(); changes per piece of content. @category SDK & Sessions */
410
417
  export interface SessionConfig {
411
- /** Full break-manifest URL. The SDK fetches exactly this URL and never rewrites it (proxied, signed or opaque endpoints all work). */
412
- manifestUrl: string;
418
+ /**
419
+ * Full break-manifest URL. The SDK fetches exactly this URL and never rewrites it (proxied,
420
+ * signed or opaque endpoints all work). When omitted, the SDK uses the ads manifest URL the
421
+ * content player discovered (see `PlayerAdapter.discoveredAdsManifestUrl`); the session then
422
+ * also auto-restarts when the player reports a different discovered URL, and ends when the
423
+ * player reports there is none.
424
+ */
425
+ manifestUrl?: string;
413
426
  /**
414
427
  * Ad insertion type for this content — how breaks relate to the content
415
428
  * timeline (DAR vs DAI). `replacement` replaces a content window; `insertion`
@@ -0,0 +1,17 @@
1
+ /** Placement of a scheduled break on the content timeline. @category Playback & UI */
2
+ export type ScheduledBreakPosition = 'pre' | 'mid' | 'post';
3
+ /** Viewer-specific opportunity for scrubber markers. @category Playback & UI */
4
+ export interface ScheduledBreak {
5
+ /** Manifest break identifier. */
6
+ id: string;
7
+ /** Placement relative to content playback. */
8
+ position: ScheduledBreakPosition;
9
+ /** Content seconds; zero for pre-roll, null for post-roll, live or unresolved PTS. */
10
+ startSec: number | null;
11
+ /** ISO 8601 start for wallclock timeline breaks; otherwise null. */
12
+ startDate: string | null;
13
+ /** Manifest break duration in seconds, not the remaining playback duration. */
14
+ durationSec: number;
15
+ /** True once this session emitted adbreakbegin for this identifier. */
16
+ played: boolean;
17
+ }
@@ -0,0 +1 @@
1
+ export{};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dolby-optiview/ads-sdk-core",
3
- "version": "1.4.0",
3
+ "version": "1.6.0",
4
4
  "description": "OptiView Ads SDK Core - Player-agnostic ad insertion",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",