@dolby-optiview/ads-sdk-core 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +68 -0
- package/dist/OptiViewAds.d.ts +1 -0
- package/dist/OptiViewAds.js +1 -1
- package/dist/diagnostics/recordEventTimeline.d.ts +3 -0
- package/dist/diagnostics/recordEventTimeline.js +1 -0
- package/dist/generated/diagnosticCodes.d.ts +96 -1
- package/dist/generated/diagnosticCodes.js +1 -1
- package/dist/generated/version.d.ts +1 -1
- package/dist/generated/version.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/interfaces/PlayerAdapter.d.ts +33 -0
- package/dist/internal/index.d.ts +1 -1
- package/dist/internal/index.js +1 -1
- package/dist/services/AdPlayerController.d.ts +3 -0
- package/dist/services/AdPlayerController.js +1 -1
- package/dist/services/BreakScheduler.d.ts +5 -2
- package/dist/services/BreakScheduler.js +1 -1
- package/dist/services/CmcdAdReporter.d.ts +10 -0
- package/dist/services/CmcdAdReporter.js +1 -1
- package/dist/services/GamPodAdTracker.d.ts +12 -1
- package/dist/services/GamPodAdTracker.js +1 -1
- package/dist/services/LensReporter.d.ts +3 -1
- package/dist/services/LensReporter.js +1 -1
- package/dist/services/LiveStartPushbackController.d.ts +36 -0
- package/dist/services/LiveStartPushbackController.js +1 -0
- package/dist/services/MetadataCueTracker.d.ts +15 -0
- package/dist/services/MetadataCueTracker.js +1 -1
- package/dist/services/VastAdManager.js +1 -1
- package/dist/services/adPlayer/AdPlayerState.d.ts +11 -0
- package/dist/services/adPlayer/AdPlayerState.js +1 -1
- package/dist/services/adPlayer/AdPreloader.d.ts +9 -1
- package/dist/services/adPlayer/AdPreloader.js +1 -1
- package/dist/services/adPlayer/AdStage.d.ts +5 -0
- package/dist/services/adPlayer/AdStage.js +1 -1
- package/dist/services/adPlayer/AssetPlayback.d.ts +15 -1
- package/dist/services/adPlayer/AssetPlayback.js +1 -1
- package/dist/services/adPlayer/ContentResumeCoordinator.d.ts +39 -0
- package/dist/services/adPlayer/ContentResumeCoordinator.js +1 -1
- package/dist/services/adPlayer/GamPodBridge.d.ts +7 -0
- package/dist/services/adPlayer/GamPodBridge.js +1 -1
- package/dist/services/adPlayer/PreparedPresentation.d.ts +32 -0
- package/dist/services/adPlayer/PreparedPresentation.js +1 -0
- package/dist/services/adPlayer/SharedElementBreakPlayer.d.ts +8 -0
- package/dist/services/adPlayer/SharedElementBreakPlayer.js +1 -1
- package/dist/services/adPlayer/classifyMediaError.d.ts +9 -0
- package/dist/services/adPlayer/classifyMediaError.js +1 -0
- package/dist/services/adPlayer/liveEdge.d.ts +29 -0
- package/dist/services/adPlayer/liveEdge.js +1 -1
- package/dist/services/anvatoCueRuntime.d.ts +7 -0
- package/dist/services/anvatoCueRuntime.js +1 -1
- package/dist/services/classifyImaAdError.d.ts +6 -0
- package/dist/services/classifyImaAdError.js +1 -0
- package/dist/services/html5ImaErrorNames.d.ts +2 -0
- package/dist/services/html5ImaErrorNames.js +1 -0
- package/dist/services/lensBackend.d.ts +1 -1
- package/dist/services/resolveLiveStartPushback.d.ts +1 -0
- package/dist/services/resolveLiveStartPushback.js +1 -0
- package/dist/session/AdBreakStatusTracker.js +1 -1
- package/dist/session/BreakLifecycle.js +1 -1
- package/dist/session/SessionStarter.d.ts +1 -0
- package/dist/session/SessionStarter.js +1 -1
- package/dist/types/AdError.d.ts +12 -0
- package/dist/types/AdError.js +1 -0
- package/dist/types/OptiViewAdsConfig.d.ts +17 -0
- package/dist/types/VendorAdError.d.ts +6 -5
- package/dist/types/VendorAdError.js +1 -1
- package/dist/vendor/lens-sdk/THIRD-PARTY-NOTICES.txt +1 -1
- package/dist/vendor/lens-sdk/index.d.ts +1 -1
- package/dist/vendor/lens-sdk/index.js +1 -1
- package/dist/vendor/lens-sdk/lens-core-uGZfZpQy.js +1 -0
- package/dist/vendor/lens-sdk/lens.d.ts +2 -0
- package/dist/vendor/lens-sdk/teardown.d.ts +13 -0
- package/dist/vendor/lens-sdk/transport.d.ts +11 -2
- package/dist/vendor/lens-sdk/types.d.ts +56 -5
- package/dist/vendor/lens-sdk/validation.d.ts +4 -1
- package/package.json +1 -1
- package/dist/vendor/lens-sdk/lens-core-Q5lbQGMC.js +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isAnvatoCue as e,parseAnvatoPayload as t}from"./MetadataCueTracker";export function armBreakFromPresentedCue(r,s,
|
|
1
|
+
import{isAnvatoCue as e,parseAnvatoPayload as t}from"./MetadataCueTracker";export function cueAnchorForBreak(e,t){const r=t.start;return e&&"number"==typeof r&&Number.isFinite(r)?e.mediaTimeForPts(r):null}export function armBreakFromPresentedCue(r,s,n){if(!s||!n||!e(r))return;const a=t(r.messageData??"");if("cue"!==a.type)return;const i=Number(a.pts);Number.isFinite(i)&&n.notifyCuePresented(i)}export function notedManifestBreakIds(e,t){const r=[];for(const s of e.breaks)t.has(s.id)||(t.add(s.id),r.push(s.id));return r}export function diagnoseAnvatoCueObserved(r,s,n,a,i,o){if(!n||!e(r))return;const u=r.messageData??"",d=t(u);if("cue"!==d.type)return;const c=`${r.startTime}|${u}`;if(a.has(c))return;a.add(c);const m=Number(d.pts);o("DA-ANVATO-CUE-OBSERVED",`Anvato cue observed: pts=${d.pts} at media time ${r.startTime}s`,{pts:Number.isFinite(m)?m:d.pts,mediaTime:r.startTime,playheadSec:i,recorded:s,payload:u.slice(0,300)})}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { KnownDiagnosticCode } from '../diagnostics/errorCodes';
|
|
2
|
+
/**
|
|
3
|
+
* Canonical CMCD `adec` code for an IMA client-side (VAST) error, by IMA `AdErrorCode` name.
|
|
4
|
+
* Unknown or absent names map to `DA-VAST-IMA-ERROR`; the raw IMA number travels in `adevc`.
|
|
5
|
+
*/
|
|
6
|
+
export declare function classifyImaAdError(name: string | null | undefined): KnownDiagnosticCode;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const A=new Map([["DA-VAST-NO-FILL",["VAST_EMPTY_RESPONSE","VAST_NO_ADS_AFTER_WRAPPER","VAST_ASSET_NOT_FOUND"]],["DA-VAST-TIMEOUT",["VAST_LOAD_TIMEOUT","VAST_MEDIA_LOAD_TIMEOUT"]],["DA-VAST-MALFORMED",["VAST_MALFORMED_RESPONSE","VAST_SCHEMA_VALIDATION_ERROR","VAST_UNSUPPORTED_VERSION","UNKNOWN_AD_RESPONSE","PLAYLIST_MALFORMED_RESPONSE"]],["DA-VAST-REQUEST-FAILED",["FAILED_TO_REQUEST_ADS","ADS_REQUEST_NETWORK_ERROR","VAST_WRAPPER_ERROR","VAST_TOO_MANY_REDIRECTS","VAST_INVALID_URL","INVALID_AD_TAG","UNSUPPORTED_URL"]],["DA-VAST-CREATIVE-FAILED",["VIDEO_PLAY_ERROR","VAST_LINEAR_ASSET_MISMATCH","VAST_PROBLEM_DISPLAYING_MEDIA_FILE","VAST_TRAFFICKING_ERROR","VAST_UNEXPECTED_LINEARITY","VAST_UNEXPECTED_DURATION","VPAID_ERROR"]]].flatMap(([A,E])=>E.map(E=>[E,A])));export function classifyImaAdError(E){return(E?A.get(E):void 0)??"DA-VAST-IMA-ERROR"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const _=new Map([[100,"VAST_MALFORMED_RESPONSE"],[101,"VAST_SCHEMA_VALIDATION_ERROR"],[102,"VAST_UNSUPPORTED_VERSION"],[200,"VAST_TRAFFICKING_ERROR"],[201,"VAST_UNEXPECTED_LINEARITY"],[202,"VAST_UNEXPECTED_DURATION"],[300,"VAST_WRAPPER_ERROR"],[301,"VAST_LOAD_TIMEOUT"],[302,"VAST_TOO_MANY_REDIRECTS"],[303,"VAST_NO_ADS_AFTER_WRAPPER"],[400,"VIDEO_PLAY_ERROR"],[402,"VAST_MEDIA_LOAD_TIMEOUT"],[403,"VAST_LINEAR_ASSET_MISMATCH"],[405,"VAST_PROBLEM_DISPLAYING_MEDIA_FILE"],[900,"UNKNOWN_ERROR"],[901,"VPAID_ERROR"],[1005,"FAILED_TO_REQUEST_ADS"],[1007,"VAST_ASSET_NOT_FOUND"],[1009,"VAST_EMPTY_RESPONSE"],[1010,"UNKNOWN_AD_RESPONSE"],[1012,"ADS_REQUEST_NETWORK_ERROR"],[1013,"INVALID_AD_TAG"],[1022,"UNSUPPORTED_URL"]]);export function html5ImaErrorName(E){return _.get(E)}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* builds (never committed, like the version stamp):
|
|
5
5
|
*
|
|
6
6
|
* main branch → production (committed default, no stamp)
|
|
7
|
-
* any other branch → development (
|
|
7
|
+
* any other branch → development (staging ingest)
|
|
8
8
|
*
|
|
9
9
|
* The committed default is production on purpose: a build that somehow skips the
|
|
10
10
|
* stamp must degrade to "reports to production", never to "release artefact
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function resolveLiveStartPushback(position: number, seekableStart: number, seekableEnd: number, targetDuration: number, minimumDistanceTargetDurations?: number): number | null;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function resolveLiveStartPushback(e,t,n,r,u=5){const i=r*u;if(![e,t,n,r,u,i].every(Number.isFinite)||r<=0||u<=0||n<=t||n-e>=86400||n-e>=i)return null;const l=Math.max(t,n-i);return l<e?l:null}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export class AdBreakStatusTracker{constructor(t){this.deps=t,this.adBreakStatus={phase:"idle",break:null,ticking:!1},this.activeBreak=null,this.activeBreakRemainingAtStart=null,this.activeBreakStartTimestamp=null,this.activeBreakElapsedSec=0,this.currentAdStartElapsedSec=0,this.currentAdAssetId=null,this.timelineAssetId=null,this.timelineStartElapsedSec=0,this.currentAdIndex=null,this.currentAdTotal=null,this.currentAdTime=null,this.currentAdDuration=null,this.activeBreakWaiting=!1,this.activeBreakPlaying=!1}getAdBreakStatus(){if("active"===this.adBreakStatus.phase)return{...this.adBreakStatus,breakRemainingSec:this.computeBreakRemainingSec()};if("upcoming"===this.adBreakStatus.phase&&this.activeBreak){const t=this.deps.secondsUntilBreak(this.activeBreak.id);return{...this.adBreakStatus,secondsUntilBreak:t}}return{...this.adBreakStatus}}onBreakWarning(t,e,i){this.activeBreak=t,this.emitAdBreakStatus({phase:"upcoming",break:t,format:this.deps.effectiveFormatOf(t),secondsUntilBreak:i,ticking:!1,warningSec:e})}setAdBreakStatusActive(t,e){this.activeBreak=t,this.activeBreakRemainingAtStart=e,this.activeBreakStartTimestamp=Date.now(),this.activeBreakElapsedSec=0,this.currentAdStartElapsedSec=0,this.currentAdAssetId=null,this.timelineAssetId=null,this.timelineStartElapsedSec=0,this.currentAdIndex=null,this.currentAdTotal=null,this.currentAdTime=null,this.currentAdDuration=null,this.activeBreakWaiting=!1,this.activeBreakPlaying=!1,this.emitAdBreakStatus({phase:"active",break:t,format:this.deps.effectiveFormatOf(t),breakRemainingSec:this.activeBreakRemainingAtStart,ticking:!1})}setAdBreakVisible(t){this.activeBreakWaiting=!1,this.activeBreakPlaying=!0,this.emitAdBreakStatus({...this.buildActiveAdBreakStatus(t),ticking:!0})}onAdStarted(t,e,i,a){this.currentAdIndex=i,this.currentAdAssetId=e.id,this.currentAdTotal=a,this.currentAdTime=null,this.currentAdDuration=null,this.currentAdStartElapsedSec=this.currentBreakElapsedSec(),this.activeBreakElapsedSec=this.currentAdStartElapsedSec,this.activeBreakWaiting=!1,this.activeBreakPlaying=!0,this.deps.breakTransitionTimer()?.settle("into-break","ad-to-ad","break-to-break"),this.emitAdBreakStatus({...this.buildActiveAdBreakStatus(t),adIndex:i,totalAds:a,adsRemaining:a-i,ticking:!0})}onAdEnded(t,e,i){this.timelineAssetId===this.currentAdAssetId&&null!==this.currentAdDuration&&Number.isFinite(this.currentAdDuration)?this.activeBreakElapsedSec=this.timelineStartElapsedSec+this.currentAdDuration:null!==this.currentAdTime&&(this.activeBreakElapsedSec=this.timelineStartElapsedSec+this.currentAdTime),this.currentAdIndex=null,this.currentAdAssetId=null,this.currentAdTime=null,this.currentAdDuration=null,this.activeBreakPlaying=!1,this.emitAdBreakStatus({...this.buildActiveAdBreakStatus(t),adsRemaining:Math.max(0,i-e-1),breakRemainingSec:this.computeBreakRemainingSec(),ticking:!1})}onAdTimeUpdated(t,e,i,a){e.id!==this.timelineAssetId&&(this.timelineAssetId=e.id,this.timelineStartElapsedSec=this.currentAdStartElapsedSec),this.currentAdTime=i,this.currentAdDuration=a,this.activeBreakElapsedSec=this.timelineStartElapsedSec+i,this.activeBreakWaiting=!1,this.activeBreakPlaying=!0,this.emitAdBreakStatus({...this.buildActiveAdBreakStatus(t),breakRemainingSec:this.computeBreakRemainingSec(),ticking:!0})}onAdPlaybackWaiting(){this.activeBreakWaiting=!0,this.activeBreakPlaying=!1,this.activeBreak&&this.emitAdBreakStatus({...this.buildActiveAdBreakStatus(this.activeBreak),ticking:!1})}onAdPlaybackPlaying(){this.activeBreakWaiting=!1,this.activeBreakPlaying=!0,this.activeBreak&&this.emitAdBreakStatus({...this.buildActiveAdBreakStatus(this.activeBreak),ticking:!0})}buildActiveAdBreakStatus(t){return{phase:"active",break:t,format:this.deps.effectiveFormatOf(t),adIndex:this.currentAdIndex??void 0,totalAds:this.currentAdTotal??void 0,adsRemaining:null!==this.currentAdIndex&&null!==this.currentAdTotal?this.currentAdTotal-this.currentAdIndex:void 0,breakRemainingSec:this.computeBreakRemainingSec(),ticking:this.activeBreakPlaying&&!this.activeBreakWaiting}}computeBreakRemainingSec(){if(null!==this.activeBreakRemainingAtStart)return Math.max(0,this.activeBreakRemainingAtStart-this.currentBreakElapsedSec())}currentBreakElapsedSec(){let t=this.activeBreakElapsedSec;return 0===t&&null!==this.activeBreakStartTimestamp&&null===this.currentAdTime&&(t=(Date.now()-this.activeBreakStartTimestamp)/1e3),t}resetAdBreakStatus(){this.activeBreak=null,this.activeBreakRemainingAtStart=null,this.activeBreakStartTimestamp=null,this.activeBreakElapsedSec=0,this.currentAdStartElapsedSec=0,this.currentAdAssetId=null,this.timelineAssetId=null,this.timelineStartElapsedSec=0,this.currentAdIndex=null,this.currentAdTotal=null,this.currentAdTime=null,this.currentAdDuration=null,this.activeBreakWaiting=!1,this.activeBreakPlaying=!1,this.emitAdBreakStatus({phase:"idle",break:null,ticking:!1})}emitAdBreakStatus(t){this.statusesEqual(this.adBreakStatus,t)||(this.adBreakStatus=t,this.deps.emit("adbreakstatus",{type:"adbreakstatus",timestamp:Date.now(),status:t}))}statusesEqual(t,e){return t.phase===e.phase&&t.format===e.format&&t.secondsUntilBreak===e.secondsUntilBreak&&t.breakRemainingSec===e.breakRemainingSec&&t.adIndex===e.adIndex&&t.totalAds===e.totalAds&&t.adsRemaining===e.adsRemaining&&t.ticking===e.ticking&&t.warningSec===e.warningSec&&t.break?.id===e.break?.id}}
|
|
1
|
+
export class AdBreakStatusTracker{constructor(t){this.deps=t,this.adBreakStatus={phase:"idle",break:null,ticking:!1},this.activeBreak=null,this.activeBreakRemainingAtStart=null,this.activeBreakStartTimestamp=null,this.activeBreakElapsedSec=0,this.currentAdStartElapsedSec=0,this.currentAdAssetId=null,this.timelineAssetId=null,this.timelineStartElapsedSec=0,this.currentAdIndex=null,this.currentAdTotal=null,this.currentAdTime=null,this.currentAdDuration=null,this.activeBreakWaiting=!1,this.activeBreakPlaying=!1}getAdBreakStatus(){if("active"===this.adBreakStatus.phase)return{...this.adBreakStatus,breakRemainingSec:this.computeBreakRemainingSec()};if("upcoming"===this.adBreakStatus.phase&&this.activeBreak){const t=this.deps.secondsUntilBreak(this.activeBreak.id);return{...this.adBreakStatus,secondsUntilBreak:t}}return{...this.adBreakStatus}}onBreakWarning(t,e,i){this.activeBreak=t,this.emitAdBreakStatus({phase:"upcoming",break:t,format:this.deps.effectiveFormatOf(t),secondsUntilBreak:i,ticking:!1,warningSec:e})}setAdBreakStatusActive(t,e){this.activeBreak=t,this.activeBreakRemainingAtStart=e,this.activeBreakStartTimestamp=Date.now(),this.activeBreakElapsedSec=0,this.currentAdStartElapsedSec=0,this.currentAdAssetId=null,this.timelineAssetId=null,this.timelineStartElapsedSec=0,this.currentAdIndex=null,this.currentAdTotal=null,this.currentAdTime=null,this.currentAdDuration=null,this.activeBreakWaiting=!1,this.activeBreakPlaying=!1,this.emitAdBreakStatus({phase:"active",break:t,format:this.deps.effectiveFormatOf(t),breakRemainingSec:this.activeBreakRemainingAtStart,ticking:!1})}setAdBreakVisible(t){this.activeBreakWaiting=!1,this.activeBreakPlaying=!0,this.emitAdBreakStatus({...this.buildActiveAdBreakStatus(t),ticking:!0})}onAdStarted(t,e,i,a){this.currentAdIndex=i,this.currentAdAssetId=e.id,this.currentAdTotal=a,this.currentAdTime=null,this.currentAdDuration=null,this.currentAdStartElapsedSec=this.currentBreakElapsedSec(),this.activeBreakElapsedSec=this.currentAdStartElapsedSec,this.activeBreakWaiting=!1,this.activeBreakPlaying=!0,this.deps.breakTransitionTimer()?.settle("into-break","ad-to-ad","break-to-break"),this.emitAdBreakStatus({...this.buildActiveAdBreakStatus(t),adIndex:i,totalAds:a,adsRemaining:a-i,ticking:!0})}onAdEnded(t,e,i){this.timelineAssetId===this.currentAdAssetId&&null!==this.currentAdDuration&&Number.isFinite(this.currentAdDuration)?this.activeBreakElapsedSec=this.timelineStartElapsedSec+this.currentAdDuration:null!==this.currentAdTime&&(this.activeBreakElapsedSec=this.timelineStartElapsedSec+this.currentAdTime),this.currentAdIndex=null,this.currentAdAssetId=null,this.currentAdTime=null,this.currentAdDuration=null,this.activeBreakPlaying=!1,this.emitAdBreakStatus({...this.buildActiveAdBreakStatus(t),adsRemaining:Math.max(0,i-e-1),breakRemainingSec:this.computeBreakRemainingSec(),ticking:!1})}onAdTimeUpdated(t,e,i,a){e.id!==this.timelineAssetId&&(this.timelineAssetId=e.id,this.timelineStartElapsedSec=null===this.currentAdIndex?this.currentBreakElapsedSec():this.currentAdStartElapsedSec),this.currentAdTime=i,this.currentAdDuration=a,this.activeBreakElapsedSec=this.timelineStartElapsedSec+i,this.activeBreakWaiting=!1,this.activeBreakPlaying=!0,this.emitAdBreakStatus({...this.buildActiveAdBreakStatus(t),breakRemainingSec:this.computeBreakRemainingSec(),ticking:!0})}onAdPlaybackWaiting(){this.activeBreakWaiting=!0,this.activeBreakPlaying=!1,this.activeBreak&&this.emitAdBreakStatus({...this.buildActiveAdBreakStatus(this.activeBreak),ticking:!1})}onAdPlaybackPlaying(){this.activeBreakWaiting=!1,this.activeBreakPlaying=!0,this.activeBreak&&this.emitAdBreakStatus({...this.buildActiveAdBreakStatus(this.activeBreak),ticking:!0})}buildActiveAdBreakStatus(t){return{phase:"active",break:t,format:this.deps.effectiveFormatOf(t),adIndex:this.currentAdIndex??void 0,totalAds:this.currentAdTotal??void 0,adsRemaining:null!==this.currentAdIndex&&null!==this.currentAdTotal?this.currentAdTotal-this.currentAdIndex:void 0,breakRemainingSec:this.computeBreakRemainingSec(),ticking:this.activeBreakPlaying&&!this.activeBreakWaiting}}computeBreakRemainingSec(){if(null!==this.activeBreakRemainingAtStart)return Math.max(0,this.activeBreakRemainingAtStart-this.currentBreakElapsedSec())}currentBreakElapsedSec(){let t=this.activeBreakElapsedSec;return 0===t&&null!==this.activeBreakStartTimestamp&&null===this.currentAdTime&&(t=(Date.now()-this.activeBreakStartTimestamp)/1e3),t}resetAdBreakStatus(){this.activeBreak=null,this.activeBreakRemainingAtStart=null,this.activeBreakStartTimestamp=null,this.activeBreakElapsedSec=0,this.currentAdStartElapsedSec=0,this.currentAdAssetId=null,this.timelineAssetId=null,this.timelineStartElapsedSec=0,this.currentAdIndex=null,this.currentAdTotal=null,this.currentAdTime=null,this.currentAdDuration=null,this.activeBreakWaiting=!1,this.activeBreakPlaying=!1,this.emitAdBreakStatus({phase:"idle",break:null,ticking:!1})}emitAdBreakStatus(t){this.statusesEqual(this.adBreakStatus,t)||(this.adBreakStatus=t,this.deps.emit("adbreakstatus",{type:"adbreakstatus",timestamp:Date.now(),status:t}))}statusesEqual(t,e){return t.phase===e.phase&&t.format===e.format&&t.secondsUntilBreak===e.secondsUntilBreak&&t.breakRemainingSec===e.breakRemainingSec&&t.adIndex===e.adIndex&&t.totalAds===e.totalAds&&t.adsRemaining===e.adsRemaining&&t.ticking===e.ticking&&t.warningSec===e.warningSec&&t.break?.id===e.break?.id}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{BreakFormat as e}from"../types/BreakManifest";import{playbackErrorContext as t}from"../diagnostics/playbackErrorContext";export class BreakLifecycle{constructor(e,t){this.state=e,this.deps=t,this.breakBeginEmittedId=null,this.pendingAdBreakBegin=null}emitAdBreakBegin(e,t){this.breakBeginEmittedId!==e.id&&(this.breakBeginEmittedId=e.id,this.pendingAdBreakBegin=null,this.deps.emit("adbreakbegin",{type:"adbreakbegin",timestamp:Date.now(),break:e,format:this.deps.effectiveFormatOf(e),elapsedSec:t?.elapsedSec??0,remainingSec:t?.remainingSec??e.duration??0,...t?.tuneIn?{tuneIn:{elapsedSec:t.elapsedSec,remainingSec:t.remainingSec}}:{}}))}flushPendingAdBreakBegin(){this.pendingAdBreakBegin?.()}async onBreakStart(
|
|
1
|
+
import{BreakFormat as e}from"../types/BreakManifest";import{playbackErrorContext as t}from"../diagnostics/playbackErrorContext";import{adErrorCode as a}from"../types/AdError";import{VendorAdError as i}from"../types/VendorAdError";export class BreakLifecycle{constructor(e,t){this.state=e,this.deps=t,this.breakBeginEmittedId=null,this.pendingAdBreakBegin=null}emitAdBreakBegin(e,t){this.breakBeginEmittedId!==e.id&&(this.breakBeginEmittedId=e.id,this.pendingAdBreakBegin=null,this.deps.emit("adbreakbegin",{type:"adbreakbegin",timestamp:Date.now(),break:e,format:this.deps.effectiveFormatOf(e),elapsedSec:t?.elapsedSec??0,remainingSec:t?.remainingSec??e.duration??0,...t?.tuneIn?{tuneIn:{elapsedSec:t.elapsedSec,remainingSec:t.remainingSec}}:{}}))}flushPendingAdBreakBegin(){this.pendingAdBreakBegin?.()}async onBreakStart(d,r){if(this.state.snapbackSeekInFlight=!1,this.deps.adPlayerController.isAdPlaying())return this.deps.diagnose("DA-BREAK-SUPPRESSED-OVERLAP",`Break suppressed (ad already playing): ${d.id}`,{breakId:d.id}),this.deps.breakScheduler.completeBreak(d.id),void this.deps.onBreakSettled?.();this.deps.adPlayerController.diagnoseUnknownVariantFormats(d);const s=this.deps.adPlayerController.breakNothingToPlayReason(d);if(s)return this.deps.log("Break not begun (no playable variant):",d.id,s),this.deps.diagnose("DA-BREAK-NO-PLAYABLE-ASSET",`break ${d.id} has no playable variant (${s}); it will not begin.`,{breakId:d.id,reason:s}),this.deps.emit("aderror",{type:"aderror",timestamp:Date.now(),source:"ad",break:d,format:this.deps.effectiveFormatOf(d),error:new Error(`Break ${d.id} has no playable variant (${s})`)}),this.deps.breakScheduler.completeBreak(d.id),void this.deps.onBreakSettled?.();this.deps.log("Break starting:",d.id,r.tuneIn?"(tune-in)":""),this.deps.breakTransitionTimer?.mark("into-break",d.id),this.breakBeginEmittedId=null,this.pendingAdBreakBegin=null;const n=this.state.heldImmediatePreRollId===d.id,o=this.deps.effectiveFormatOf(d),k=o===e.SINGLE||o===e.LSHAPE_AD,p=n&&k;n&&(this.state.heldImmediatePreRollId=null),this.deps.adBreakStatusTracker.setAdBreakStatusActive(d,void 0!==r.remainingSec?r.remainingSec:d.duration),p||this.emitAdBreakBegin(d,r),this.deps.manifestService.setActive(!0),this.deps.breakScheduler.claimChain(d.id);const h={onAdStart:(e,t,a,i,d)=>{this.deps.adBreakStatusTracker.onAdStarted(e,t,a,i),this.deps.emit("adbegin",{type:"adbegin",timestamp:Date.now(),break:e,format:this.deps.effectiveFormatOf(e),asset:t,adIndex:a,totalAds:i,...d?.adId?{adId:d.adId}:{},...d?.creativeId?{creativeId:d.creativeId}:{},...void 0!==d?.podPosition?{podPosition:d.podPosition}:{}})},onAdEnd:(e,t,a,i,d)=>{this.deps.adBreakStatusTracker.onAdEnded(e,a,i),a+1<i&&this.deps.breakTransitionTimer?.mark("ad-to-ad",e.id),this.deps.emit("adend",{type:"adend",timestamp:Date.now(),break:e,format:this.deps.effectiveFormatOf(e),asset:t,adIndex:a,totalAds:i,...d?.adId?{adId:d.adId}:{},...d?.creativeId?{creativeId:d.creativeId}:{}})},onAdError:(e,d,r)=>{this.flushPendingAdBreakBegin(),this.deps.diagnose("DA-AD-PLAYBACK-ERROR",r.message,{breakId:e.id,assetId:d?.id,adErrorCode:a(r),...r instanceof i?{vendorCode:r.vendorCode}:{},...t(r)}),this.deps.emit("aderror",{type:"aderror",timestamp:Date.now(),source:"ad",break:e,format:this.deps.effectiveFormatOf(e),...d?{asset:d}:{},error:r})},onAdTimeUpdate:(e,t,a,i)=>{this.deps.adBreakStatusTracker.onAdTimeUpdated(e,t,a,i),this.deps.emit("adtimeupdate",{type:"adtimeupdate",timestamp:Date.now(),break:e,format:this.deps.effectiveFormatOf(e),asset:t,currentTime:a,duration:i})},onAdWaiting:(e,t)=>{this.deps.adBreakStatusTracker.onAdPlaybackWaiting(),this.deps.emit("waiting",{type:"waiting",timestamp:Date.now(),source:"ad",break:e,asset:t})},onAdPlaying:(e,t)=>{this.deps.adBreakStatusTracker.onAdPlaybackPlaying(),this.deps.emit("playing",{type:"playing",timestamp:Date.now(),source:"ad",break:e,asset:t})},onBreakEnd:e=>{this.onBreakEnd(e)},onBreakCut:e=>{this.deps.cmcdAdReporter.onBreakCut(e.id)},onBreakEndedWithoutContentPause:()=>{this.onBreakEndedWithoutContentPause()},onChainedBreakBegin:e=>{this.onChainedBreakBegin(e)},onChainedBreakSkipped:e=>{this.deps.log("Chained break skipped (no playable variant):",e.id),this.deps.breakScheduler.completeBreak(e.id)},onContentResumeSeek:(e,t)=>{this.deps.breakScheduler.notifyResumeSeek(e,t)},onContentReloading:e=>{this.deps.breakScheduler.setContentReloading(e)}};p&&(this.pendingAdBreakBegin=()=>this.emitAdBreakBegin(d,r),h.onBreakVisible=e=>{this.deps.adBreakStatusTracker.setAdBreakVisible(e),this.flushPendingAdBreakBegin()}),await this.deps.adPlayerController.playBreak(d,h,{effectiveDurationSec:r.remainingSec,firstFrameGate:p})}onChainedBreakBegin(e){this.deps.log("Chained break starting:",e.id),this.deps.breakTransitionTimer?.mark("break-to-break",e.id),this.deps.breakScheduler.activateChainedBreak(e.id),this.deps.manifestService.setActive(!0),this.deps.adBreakStatusTracker.setAdBreakStatusActive(e,e.duration),this.emitAdBreakBegin(e)}onBreakEnd(e){this.deps.log("Break ended:",e.id),this.deps.breakTransitionTimer?.mark("out-of-break",e.id),this.deps.breakScheduler.completeBreak(e.id),this.deps.manifestService.setActive(!1),this.deps.adBreakStatusTracker.resetAdBreakStatus(),this.flushPendingAdBreakBegin(),this.deps.emit("adbreakend",{type:"adbreakend",timestamp:Date.now(),break:e,format:this.deps.effectiveFormatOf(e)}),this.deps.onBreakSettled?.()}onBreakEndedWithoutContentPause(){this.deps.breakTransitionTimer?.settle("out-of-break"),this.deps.emit("playing",{type:"playing",timestamp:Date.now(),source:"content"})}onBreakAborted(e){if(this.deps.adPlayerController.isAdPlaying()){const t=this.deps.adBreakStatusTracker.currentBreakElapsedSec();this.deps.log(`Early return — ending break '${e.id}' at ${t.toFixed(2)}s`),this.deps.cmcdAdReporter.onBreakAborted(e.id),this.deps.adPlayerController.abortBreak(t)}else this.deps.breakScheduler.completeBreak(e.id),this.deps.onBreakSettled?.()}reportAbandonedBreak(e){const t=this.deps.adBreakStatusTracker.activeBreak;if(!t)return;const a=this.deps.adBreakStatusTracker.currentAdIndex;this.deps.diagnose("DA-BREAK-ABANDONED",`Session torn down (${e}) while break ${t.id} was still in flight`+(null!==a?"; an ad began and will never end.":"."),{breakId:t.id,cause:e,phase:this.deps.adBreakStatusTracker.adBreakStatus.phase,unbalancedAdBegin:null!==a,...null!==a?{adIndex:a}:{},...null!==this.deps.adBreakStatusTracker.currentAdTotal?{totalAds:this.deps.adBreakStatusTracker.currentAdTotal}:{}})}}
|
|
@@ -16,6 +16,7 @@ export interface SessionStarterDeps {
|
|
|
16
16
|
mode: OptiViewAdsMode;
|
|
17
17
|
hasExplicitMode: boolean;
|
|
18
18
|
continueContentDuringBreak: boolean;
|
|
19
|
+
startLivePushback: () => void;
|
|
19
20
|
manifestService: ManifestService | undefined;
|
|
20
21
|
breakScheduler: BreakScheduler | undefined;
|
|
21
22
|
adPlayerController: AdPlayerController | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{getPreRollDelay as e}from"../types/BreakManifest";import{manifestHasGamAssets as s,resolveGamVendorConfiguration as t}from"../services/resolveGamVendorConfiguration";import{SsaiController as i}from"../services/SsaiController";import{resolveUrlOrigin as a}from"../services/urlOrigin";import{filterSsaiClientBreaks as r,resolveStitchedStreamVendor as n,SsaiUnavailableError as o}from"../services/resolveDeliveryMode";import{detectDeviceType as d}from"../services/detectDeviceType";import{decideDeliveryMode as l,detectChannelSwitch as h,gamPodIdentityChanged as c}from"../services/channelSwitch";import{prepareWebRuntime as m}from"../services/webCompatibility";const p=1e4;function S(e){return"stitcherUrl"in e}function u(e){return"string"==typeof e&&""!==e.trim()}export class SessionStarter{constructor(e,s){this.state=e,this.deps=s}async startSession(s){if("ssai"===this.deps.mode){if(!S(s)||!u(s.stitcherUrl))throw new Error('[OptiViewAds] mode "ssai" requires a stitcherUrl in startSession().')}else if(S(s)||!u(s.manifestUrl))throw new Error("[OptiViewAds] startSession requires a manifestUrl (the full break-manifest URL).");this.state.sessionActive&&(this.deps.log("Ending previous session before starting new one"),this.deps.endSessionInternal("session-restart"));const t=this.state.sessionGeneration;this.state.sessionActive=!0;try{const e=m();e&&await e}catch(e){if(this.isStaleStart(t))return;throw this.state.sessionActive=!1,e}if(this.isStaleStart(t))return;if(this.deps.lensReporter.startSession(),S(s))return s.adInsertionType&&this.deps.diagnose("DA-INSERTION-TYPE-IGNORED-SSAI",`adInsertionType "${s.adInsertionType}" ignored in SSAI mode; the stitcher controls ad insertion.`,{adInsertionType:s.adInsertionType}),void await this.startSsaiSession(s,t);const i=s.manifestUrl;this.state.sessionConfig=s,this.state.manifestOrigin=a(i),this.deps.manifestService.setManifestUrl(i),this.deps.log("Session starting, manifest URL:",i);const r=Date.now(),n=e=>{this.deps.diagnose("DA-SESSION-STARTING",`Session start: ${e}`,{step:e,manifestUrl:i,elapsedMs:Date.now()-r})};n("begin");const o=s.assetParameters;let d;this.deps.adPlayerController.setSessionAssetParameters(o),this.deps.adPlayerController.setSessionAssetParameterMacros(s.assetParameterMacros),this.state.configSummary.gam&&(this.state.configSummary.gam={...this.state.configSummary.gam,hasAssetParameters:!!o}),n("manifest-fetch");try{d=await this.deps.manifestService.fetchManifest()}catch(e){if(this.isStaleStart(t))return;throw this.failSessionStart(e)}if(this.deps.cmcdAdReporter.onSessionStart(d),!this.isStaleStart(t))if(this.state.sessionManifest=d,"ssai"!==this.resolveSessionDeliveryMode(d))try{if(await this.startGamForManifest(d,o,n),this.isStaleStart(t))return;const a=s.adInsertionType;this.deps.adPlayerController.setBreakResumePolicy(a,d.timebase,this.deps.continueContentDuringBreak),this.deps.diagnose("DA-INSERTION-TYPE-RESOLVED",void 0===a?`Ad insertion type: unset — the manifest's resumeOffset drives the resume point (timebase: ${d.timebase})`:`Ad insertion type: ${a}, client-set (timebase: ${d.timebase})`,{adInsertionType:a??null,source:void 0===a?"unset":"client",timebase:d.timebase}),n("scheduling"),this.deps.breakScheduler.updateManifest(d),this.deps.pauseAdController.updateManifest(d),this.state.pauseAdTickInterval=setInterval(()=>this.deps.pauseAdController?.tick(),250);const r=(d.adStartDelay??0)>0?void 0:d.breaks.find(s=>0===e(s));r?(this.deps.adPlayerController.holdContentForPreRoll(r),this.state.heldImmediatePreRollId=r.id,this.state.heldPreRollArmed=!1,this.state.heldPreRollWaitStartedAt=Date.now(),this.contentIsReady()?this.armHeldImmediatePreRoll():this.state.heldPreRollArmInterval=setInterval(()=>{this.contentIsReady()?this.armHeldImmediatePreRoll():this.armHeldImmediatePreRollIfTimedOut()},250)):this.markPlaybackStartIfPlaying(),this.startBreakScheduler(),this.startManifestPolling(),this.deps.diagnose("DA-SESSION-STARTED",`Session active for manifest ${i}`,{manifestUrl:i}),this.deps.log("Session active")}catch(e){if(this.isStaleStart(t))return;throw this.failSessionStart(e)}else await this.startSteeredSsaiSession(d,s,n)}markPlaybackStartIfPlaying(){this.deps.player.paused||this.deps.breakScheduler.markPlaybackStart()}contentIsReady(){return!Number.isNaN(this.deps.player.duration)||!this.deps.player.paused}clearHeldImmediatePreRollArmInterval(){this.state.heldPreRollArmInterval&&(clearInterval(this.state.heldPreRollArmInterval),this.state.heldPreRollArmInterval=null)}armHeldImmediatePreRoll(){this.state.heldImmediatePreRollId&&!this.state.heldPreRollArmed&&(this.clearHeldImmediatePreRollArmInterval(),this.state.heldPreRollArmed=!0,this.deps.breakScheduler.markPlaybackStart())}armHeldImmediatePreRollIfTimedOut(){if(!this.state.heldImmediatePreRollId||this.state.heldPreRollArmed)return;const e=this.state.heldPreRollWaitStartedAt;if(null===e||Date.now()-e<p)return;const s=this.state.heldImmediatePreRollId;this.armHeldImmediatePreRoll(),this.deps.diagnose("DA-PREROLL-CONTENT-WAIT-TIMEOUT","Pending pre-roll began after waiting 10000ms for content readiness.",{breakId:s,waitMs:p})}cancelHeldImmediatePreRollOnContentError(){const e=this.state.heldImmediatePreRollId;return!(!e||this.state.heldPreRollArmed)&&(this.clearHeldImmediatePreRollArmInterval(),this.deps.breakScheduler?.completeBreak(e),this.deps.adPlayerController?.releaseContentHold(),this.state.heldImmediatePreRollId=null,this.state.heldPreRollArmed=!1,this.state.heldPreRollWaitStartedAt=null,this.deps.diagnose("DA-PREROLL-CANCELLED-CONTENT-ERROR","A pending pre-roll was cancelled because the content failed to load before it could begin.",{breakId:e}),!0)}clearHeldImmediatePreRollArm(){this.clearHeldImmediatePreRollArmInterval(),this.state.heldImmediatePreRollId=null,this.state.heldPreRollArmed=!1,this.state.heldPreRollWaitStartedAt=null}failSessionStart(e){const s=e instanceof Error?e:new Error(String(e)),t=this.deps.manifestService?.takeLastFailure();return this.deps.diagnose("DA-MANIFEST-FETCH-FAILED",s.message,t?{...t}:void 0),this.state.sessionActive=!1,this.deps.cmcdAdReporter.onSessionEnd(),this.deps.lensReporter.endSession(),s}resolveSessionDeliveryMode(e,s="manifest"){const t=this.state.sessionDeliveryMode,i=l(e,d(),this.deps.hasExplicitMode?this.deps.mode:null,s);return null!==i.unknownMode&&this.deps.diagnose("DA-DELIVERY-MODE-UNKNOWN",`Delivery rule ${i.ruleIndex} carries unrecognized mode "${i.unknownMode}"; using sgai.`,{unknownMode:i.unknownMode,ruleIndex:i.ruleIndex}),this.deps.hasExplicitMode&&i.manifestMode!==i.mode&&this.deps.diagnose("DA-DELIVERY-MODE-OVERRIDDEN",`Manifest delivery rules resolve "${i.manifestMode}" but the explicit config mode "${i.mode}" takes precedence.`,{manifestMode:i.manifestMode,configuredMode:i.mode}),this.state.sessionDeliveryMode=i.mode,this.deps.diagnose("DA-DELIVERY-MODE-RESOLVED",`Delivery mode: ${i.mode}`,{mode:i.mode,ruleIndex:i.ruleIndex,source:i.source,..."channel-switch"===s?{previousMode:t}:{}}),i.mode}async startSteeredSsaiSession(e,s,t){const i=n(e.vendorConfiguration);if(!i){const e="Manifest selected ssai delivery but no vendor configuration provides a stitched stream (no gam entry with an assetKey).";throw this.deps.diagnose("DA-SSAI-UNAVAILABLE",e),this.deps.manifestService.reset(),this.state.sessionActive=!1,new o(e)}try{t("ssai-live-stream"),this.state.steeredSsaiActive=!0,this.state.ssaiController=this.createSsaiController(i.networkCode),await this.state.ssaiController.startSteered(i,s.assetParameters,s.assetParameterMacros),t("scheduling");const a=r(e);this.deps.adPlayerController.setBreakResumePolicy(s.adInsertionType,e.timebase,!1),this.deps.breakScheduler.updateManifest(a),this.deps.pauseAdController.updateManifest(a),this.state.pauseAdTickInterval=setInterval(()=>this.deps.pauseAdController?.tick(),250),this.markPlaybackStartIfPlaying(),this.startBreakScheduler(),this.startManifestPolling(),this.deps.diagnose("DA-SESSION-STARTED",`SSAI session active (manifest-steered) for ${s.manifestUrl}`,{manifestUrl:s.manifestUrl}),this.deps.log("SSAI session active (manifest-steered)")}catch(e){const s=e instanceof Error?e:new Error(String(e));throw this.deps.diagnose("DA-SSAI-SESSION-FAILED",s.message),this.state.steeredSsaiActive=!1,this.deps.manifestService.reset(),this.state.sessionActive=!1,s}}createSsaiController(e){return new i(this.deps.player,{gam:this.state.steeredSsaiOptions?.streamActivityMonitorId?{streamActivityMonitorId:this.state.steeredSsaiOptions.streamActivityMonitorId}:{},ssai:{networkCode:e},maxBitrate:this.state.steeredSsaiOptions?.maxBitrate,autoplay:this.state.steeredSsaiOptions?.autoplay,debug:this.deps.debug},{emit:(e,s)=>this.deps.emit(e,s),diagnose:(e,s,t)=>this.deps.diagnose(e,s,t),log:(...e)=>this.deps.log(...e)})}startBreakScheduler(){this.deps.breakScheduler.start((e,s)=>{this.deps.breakLifecycle.onBreakStart(e,s)},e=>!this.deps.adPlayerController.isAdPlaying()&&(this.deps.adPlayerController.preload(e).catch(s=>{this.deps.diagnose("DA-PRELOAD-FAILED",s instanceof Error?s.message:String(s),{breakId:e.id})}),!0),(e,s,t)=>{this.deps.adBreakStatusTracker.onBreakWarning(e,s,t)},e=>{this.deps.breakLifecycle.onBreakAborted(e)})}startManifestPolling(){this.deps.manifestService.startPolling(e=>{if(!this.state.sessionActive)return;const s=this.deps.notedManifestBreakIds(e);this.deps.diagnose("DA-MANIFEST-POLLED","Manifest polled",{breaks:e.breaks.length,newBreakIds:s,pollingIntervalSeconds:this.deps.manifestService.currentPollingIntervalSeconds});const t=this.state.sessionManifest,i=t?h(t,e):null;if(this.state.pendingChannelSwitch||i&&this.deps.breakScheduler.getActiveBreak())return this.state.pendingChannelSwitch=e,void this.deps.log("Channel switch deferred until the active break ends");i?this.switchChannel(e,i):this.applyManifestUpdate(e)},e=>{this.state.sessionActive&&this.deps.diagnose("DA-MANIFEST-POLL-FAILED",e.message)})}applyPendingChannelSwitch(){if(!this.state.sessionActive||!this.state.pendingChannelSwitch)return;const e=this.state.pendingChannelSwitch;this.state.pendingChannelSwitch=null;const s=h(this.state.sessionManifest,e);s?this.switchChannel(e,s):this.applyManifestUpdate(e)}applyManifestUpdate(e){this.diagnoseIgnoredDeliveryModeChange(e);const s=t(e);this.state.steeredSsaiActive?this.state.ssaiController?.setManifestAssetParameters(s.ssai?.assetParameters):this.deps.adPlayerController.setManifestAssetParameters(s.sgaiAssetParameters);const i=this.state.steeredSsaiActive?r(e):e;this.deps.breakScheduler.updateManifest(i),this.deps.pauseAdController.updateManifest(i)}diagnoseIgnoredDeliveryModeChange(e){if(!this.state.sessionDeliveryMode)return;const s=l(e,d(),null,"manifest");s.manifestMode!==this.state.sessionDeliveryMode&&this.deps.diagnose("DA-DELIVERY-MODE-CHANGE-IGNORED",`Polled manifest resolves delivery mode "${s.manifestMode}" but "${this.state.sessionDeliveryMode}" is pinned for this session.`,{polledMode:s.manifestMode,pinnedMode:this.state.sessionDeliveryMode})}async switchChannel(e,s){if(this.state.channelSwitchInFlight)return;const t=this.state.sessionConfig,i=this.state.sessionManifest,a={previousChannelId:s.previousChannelId,channelId:s.channelId,reason:s.reason,manifestUrl:t.manifestUrl};this.deps.diagnose("DA-MANIFEST-CHANNEL-CHANGED",`Manifest channel changed (${s.reason}): ${s.previousChannelId??"none"} -> ${s.channelId??"none"}`,a),this.state.sessionManifest=e,this.state.channelSwitchInFlight=!0;const r=this.state.sessionGeneration,n=this.state.sessionDeliveryMode??"sgai";try{let s=this.resolveSessionDeliveryMode(e,"channel-switch");if(this.deps.cmcdAdReporter.onSessionEnd(),this.deps.cmcdAdReporter.onSessionStart(e),this.deps.lensReporter.endSession(),this.deps.lensReporter.startSession(),"ssai"===s&&"sgai"===n){const a=await this.switchToSteeredSsai(e,t);if(this.isStaleStart(r))return;a||(s="sgai",this.state.sessionDeliveryMode="sgai",await this.applySameModeChannelSwitch(i,e,t,r))}else"sgai"===s&&"ssai"===n?await this.switchToSgai(e,t,r):await this.applySameModeChannelSwitch(i,e,t,r);if(this.isStaleStart(r))return}finally{this.state.channelSwitchInFlight=!1}this.emitChannelChange({...a,previousDeliveryMode:n,deliveryMode:this.state.sessionDeliveryMode??n})}emitChannelChange(e){this.deps.emit("adchannelchange",{type:"adchannelchange",timestamp:Date.now(),...e})}async applySameModeChannelSwitch(e,s,i,a){const n=this.state.steeredSsaiActive,o=n?r(s):s;if(this.deps.breakScheduler.resetForChannelSwitch(o),this.deps.pauseAdController.updateManifest(o),this.deps.adPlayerController.setBreakResumePolicy(i.adInsertionType,s.timebase,this.deps.continueContentDuringBreak),!n&&c(e,s)){const e=this.deps.adPlayerController.updatedAssetParameters,t=this.deps.adPlayerController.sessionAssetParameterMacros;if(this.deps.adPlayerController.endGamSession(),this.deps.adPlayerController.setSessionAssetParameters(i.assetParameters),this.deps.adPlayerController.setSessionAssetParameterMacros(t),await this.startGamForManifest(s,i.assetParameters,e=>{this.deps.diagnose("DA-SESSION-STARTING",`Channel switch: ${e}`,{step:e,manifestUrl:i.manifestUrl})}),this.isStaleStart(a))return;e&&this.deps.adPlayerController.updateAssetParameters(e)}else if(n){const e=t(s);this.state.ssaiController?.setManifestAssetParameters(e.ssai?.assetParameters)}else{const e=t(s);this.deps.adPlayerController.setManifestAssetParameters(e.sgaiAssetParameters)}}async switchToSteeredSsai(e,s){const t=n(e.vendorConfiguration);if(!t)return this.deps.diagnose("DA-SSAI-UNAVAILABLE","Channel switch resolved ssai delivery but no vendor configuration provides a stitched stream; the session stays in sgai.",{manifestUrl:s.manifestUrl,previousMode:"sgai"}),!1;const i=this.deps.adPlayerController,a=i.updatedAssetParameters,o=i.sessionAssetParameterMacros;i.stopActiveBreak(),i.releaseContentHold(),i.endGamSession(),i.setSessionAssetParameters(s.assetParameters),i.setSessionAssetParameterMacros(o),a&&i.updateAssetParameters(a);const d=this.createSsaiController(t.networkCode);this.state.ssaiController=d,this.state.steeredSsaiActive=!0,this.deps.diagnose("DA-SESSION-STARTING","Channel switch: ssai-live-stream",{step:"ssai-live-stream",manifestUrl:s.manifestUrl});try{await d.startSteered(t,s.assetParameters,o),a&&d.updateAssetParameters(a);const n=r(e);return this.deps.breakScheduler.resetForChannelSwitch(n),this.deps.pauseAdController.updateManifest(n),i.setBreakResumePolicy(s.adInsertionType,e.timebase,!1),!0}catch(e){const s=e instanceof Error?e.message:String(e);return this.deps.diagnose("DA-SSAI-SESSION-FAILED",s),d.stop(),delete this.state.ssaiController,this.state.steeredSsaiActive=!1,!1}}async switchToSgai(e,s,t){const i=this.state.ssaiController?.sessionAssetParameterMacros;this.state.ssaiController?.stop(),delete this.state.ssaiController,this.state.steeredSsaiActive=!1,this.deps.breakScheduler.resetForChannelSwitch(e),this.deps.pauseAdController.updateManifest(e);const a=this.deps.adPlayerController;a.setBreakResumePolicy(s.adInsertionType,e.timebase,this.deps.continueContentDuringBreak);const r=a.updatedAssetParameters;a.endGamSession(),a.setSessionAssetParameters(s.assetParameters),a.setSessionAssetParameterMacros(i??s.assetParameterMacros),await this.startGamForManifest(e,s.assetParameters,e=>{this.deps.diagnose("DA-SESSION-STARTING",`Channel switch: ${e}`,{step:e,manifestUrl:s.manifestUrl})}),this.isStaleStart(t)||r&&a.updateAssetParameters(r)}async startGamForManifest(e,i,a){const r=t(e);if(this.deps.adPlayerController.setManifestAssetParameters(r.sgaiAssetParameters),r.unknownTypes.length>0&&this.deps.diagnose("DA-VENDOR-CONFIG-UNKNOWN-TYPE",`Ignored ${r.unknownTypes.length} vendorConfiguration `+(1===r.unknownTypes.length?"entry":"entries")+" with unrecognised type: "+`${r.unknownTypes.join(", ")}.`,{types:r.unknownTypes}),this.deps.adPlayerController.isGamEnabled())if(r.identity){this.deps.diagnose("DA-GAM-CONFIG-RESOLVED",`GAM session identity: network ${r.identity.networkCode}, custom asset ${r.identity.customAssetKey}`,{networkCode:r.identity.networkCode,customAssetKey:r.identity.customAssetKey,channelId:e.channelId,duplicateEntriesIgnored:r.duplicateGam});try{a("gam-init"),await this.deps.adPlayerController.startGamSession(r.identity,i),this.deps.log("GAM session started")}catch(e){this.deps.diagnose("DA-GAM-SESSION-FAILED",e instanceof Error?e.message:String(e))}}else s(e)&&this.deps.diagnose("DA-GAM-CONFIG-MISSING",'The break manifest carries GAM pod assets but no usable "gam" vendorConfiguration entry; those assets will be skipped.',{reason:r.reason});else s(e)&&this.deps.diagnose("DA-GAM-CONFIG-MISSING","The break manifest carries GAM pod assets but GAM is disabled for this session (gam.enabled === false); those assets will be skipped.",{reason:"gam-disabled"})}async startSsaiSession(e,s){try{if(this.state.stitcherOrigin=a(e.stitcherUrl),this.state.configSummary.gam&&(this.state.configSummary.gam={...this.state.configSummary.gam,hasAssetParameters:!!e.assetParameters}),await this.state.ssaiController.start(e),this.isStaleStart(s))return;this.deps.diagnose("DA-SESSION-STARTED",`SSAI session active for ${e.stitcherUrl}`,{stitcherUrl:e.stitcherUrl}),this.deps.log("SSAI session active")}catch(e){if(this.isStaleStart(s))return;const t=e instanceof Error?e:new Error(String(e));throw this.deps.diagnose("DA-SSAI-SESSION-FAILED",t.message),this.state.sessionActive=!1,this.deps.cmcdAdReporter.onSessionEnd(),this.deps.lensReporter.endSession(),t}}isStaleStart(e){return e!==this.state.sessionGeneration&&(this.deps.log("Session start aborted — the session ended or was replaced while starting"),!0)}}
|
|
1
|
+
import{getPreRollDelay as e}from"../types/BreakManifest";import{manifestHasGamAssets as s,resolveGamVendorConfiguration as t}from"../services/resolveGamVendorConfiguration";import{SsaiController as i}from"../services/SsaiController";import{resolveUrlOrigin as a}from"../services/urlOrigin";import{filterSsaiClientBreaks as r,resolveStitchedStreamVendor as n,SsaiUnavailableError as o}from"../services/resolveDeliveryMode";import{detectDeviceType as d}from"../services/detectDeviceType";import{decideDeliveryMode as l,detectChannelSwitch as h,gamPodIdentityChanged as c}from"../services/channelSwitch";import{prepareWebRuntime as m}from"../services/webCompatibility";const p=1e4;function S(e){return"stitcherUrl"in e}function u(e){return"string"==typeof e&&""!==e.trim()}export class SessionStarter{constructor(e,s){this.state=e,this.deps=s}async startSession(s){if("ssai"===this.deps.mode){if(!S(s)||!u(s.stitcherUrl))throw new Error('[OptiViewAds] mode "ssai" requires a stitcherUrl in startSession().')}else if(S(s)||!u(s.manifestUrl))throw new Error("[OptiViewAds] startSession requires a manifestUrl (the full break-manifest URL).");this.state.sessionActive&&(this.deps.log("Ending previous session before starting new one"),this.deps.endSessionInternal("session-restart"));const t=this.state.sessionGeneration;this.state.sessionActive=!0;try{const e=m();e&&await e}catch(e){if(this.isStaleStart(t))return;throw this.state.sessionActive=!1,e}if(this.isStaleStart(t))return;if(this.deps.lensReporter.startSession(),S(s))return s.adInsertionType&&this.deps.diagnose("DA-INSERTION-TYPE-IGNORED-SSAI",`adInsertionType "${s.adInsertionType}" ignored in SSAI mode; the stitcher controls ad insertion.`,{adInsertionType:s.adInsertionType}),void await this.startSsaiSession(s,t);const i=s.manifestUrl;this.state.sessionConfig=s,this.state.manifestOrigin=a(i),this.deps.manifestService.setManifestUrl(i),this.deps.log("Session starting, manifest URL:",i);const r=Date.now(),n=e=>{this.deps.diagnose("DA-SESSION-STARTING",`Session start: ${e}`,{step:e,manifestUrl:i,elapsedMs:Date.now()-r})};n("begin");const o=s.assetParameters;let d;this.deps.adPlayerController.setSessionAssetParameters(o),this.deps.adPlayerController.setSessionAssetParameterMacros(s.assetParameterMacros),this.state.configSummary.gam&&(this.state.configSummary.gam={...this.state.configSummary.gam,hasAssetParameters:!!o}),n("manifest-fetch");try{d=await this.deps.manifestService.fetchManifest()}catch(e){if(this.isStaleStart(t))return;throw this.failSessionStart(e)}if(this.deps.cmcdAdReporter.onSessionStart(d),!this.isStaleStart(t))if(this.state.sessionManifest=d,"ssai"!==this.resolveSessionDeliveryMode(d))try{if(await this.startGamForManifest(d,o,n),this.isStaleStart(t))return;const a=s.adInsertionType;this.deps.adPlayerController.setBreakResumePolicy(a,d.timebase,this.deps.continueContentDuringBreak),this.deps.diagnose("DA-INSERTION-TYPE-RESOLVED",void 0===a?`Ad insertion type: unset — the manifest's resumeOffset drives the resume point (timebase: ${d.timebase})`:`Ad insertion type: ${a}, client-set (timebase: ${d.timebase})`,{adInsertionType:a??null,source:void 0===a?"unset":"client",timebase:d.timebase}),n("scheduling"),this.deps.breakScheduler.updateManifest(d),this.deps.pauseAdController.updateManifest(d),this.state.pauseAdTickInterval=setInterval(()=>this.deps.pauseAdController?.tick(),250);const r=(d.adStartDelay??0)>0?void 0:d.breaks.find(s=>0===e(s));r?(this.deps.adPlayerController.holdContentForPreRoll(r),this.state.heldImmediatePreRollId=r.id,this.state.heldPreRollArmed=!1,this.state.heldPreRollWaitStartedAt=Date.now(),this.contentIsReady()?this.armHeldImmediatePreRoll():this.state.heldPreRollArmInterval=setInterval(()=>{this.contentIsReady()?this.armHeldImmediatePreRoll():this.armHeldImmediatePreRollIfTimedOut()},250)):this.markPlaybackStartIfPlaying(),this.deps.startLivePushback(),this.startBreakScheduler(),this.startManifestPolling(),this.deps.diagnose("DA-SESSION-STARTED",`Session active for manifest ${i}`,{manifestUrl:i}),this.deps.log("Session active")}catch(e){if(this.isStaleStart(t))return;throw this.failSessionStart(e)}else await this.startSteeredSsaiSession(d,s,n)}markPlaybackStartIfPlaying(){this.deps.player.paused||this.deps.breakScheduler.markPlaybackStart()}contentIsReady(){return!Number.isNaN(this.deps.player.duration)||!this.deps.player.paused}clearHeldImmediatePreRollArmInterval(){this.state.heldPreRollArmInterval&&(clearInterval(this.state.heldPreRollArmInterval),this.state.heldPreRollArmInterval=null)}armHeldImmediatePreRoll(){this.state.heldImmediatePreRollId&&!this.state.heldPreRollArmed&&(this.clearHeldImmediatePreRollArmInterval(),this.state.heldPreRollArmed=!0,this.deps.breakScheduler.markPlaybackStart())}armHeldImmediatePreRollIfTimedOut(){if(!this.state.heldImmediatePreRollId||this.state.heldPreRollArmed)return;const e=this.state.heldPreRollWaitStartedAt;if(null===e||Date.now()-e<p)return;const s=this.state.heldImmediatePreRollId;this.armHeldImmediatePreRoll(),this.deps.diagnose("DA-PREROLL-CONTENT-WAIT-TIMEOUT","Pending pre-roll began after waiting 10000ms for content readiness.",{breakId:s,waitMs:p})}cancelHeldImmediatePreRollOnContentError(){const e=this.state.heldImmediatePreRollId;return!(!e||this.state.heldPreRollArmed)&&(this.clearHeldImmediatePreRollArmInterval(),this.deps.breakScheduler?.completeBreak(e),this.deps.adPlayerController?.releaseContentHold(),this.state.heldImmediatePreRollId=null,this.state.heldPreRollArmed=!1,this.state.heldPreRollWaitStartedAt=null,this.deps.diagnose("DA-PREROLL-CANCELLED-CONTENT-ERROR","A pending pre-roll was cancelled because the content failed to load before it could begin.",{breakId:e}),!0)}clearHeldImmediatePreRollArm(){this.clearHeldImmediatePreRollArmInterval(),this.state.heldImmediatePreRollId=null,this.state.heldPreRollArmed=!1,this.state.heldPreRollWaitStartedAt=null}failSessionStart(e){const s=e instanceof Error?e:new Error(String(e)),t=this.deps.manifestService?.takeLastFailure();return this.deps.diagnose("DA-MANIFEST-FETCH-FAILED",s.message,t?{...t}:void 0),this.state.sessionActive=!1,this.deps.cmcdAdReporter.onSessionEnd(),this.deps.lensReporter.endSession(),s}resolveSessionDeliveryMode(e,s="manifest"){const t=this.state.sessionDeliveryMode,i=l(e,d(),this.deps.hasExplicitMode?this.deps.mode:null,s);return null!==i.unknownMode&&this.deps.diagnose("DA-DELIVERY-MODE-UNKNOWN",`Delivery rule ${i.ruleIndex} carries unrecognized mode "${i.unknownMode}"; using sgai.`,{unknownMode:i.unknownMode,ruleIndex:i.ruleIndex}),this.deps.hasExplicitMode&&i.manifestMode!==i.mode&&this.deps.diagnose("DA-DELIVERY-MODE-OVERRIDDEN",`Manifest delivery rules resolve "${i.manifestMode}" but the explicit config mode "${i.mode}" takes precedence.`,{manifestMode:i.manifestMode,configuredMode:i.mode}),this.state.sessionDeliveryMode=i.mode,this.deps.diagnose("DA-DELIVERY-MODE-RESOLVED",`Delivery mode: ${i.mode}`,{mode:i.mode,ruleIndex:i.ruleIndex,source:i.source,..."channel-switch"===s?{previousMode:t}:{}}),i.mode}async startSteeredSsaiSession(e,s,t){const i=n(e.vendorConfiguration);if(!i){const e="Manifest selected ssai delivery but no vendor configuration provides a stitched stream (no gam entry with an assetKey).";throw this.deps.diagnose("DA-SSAI-UNAVAILABLE",e),this.deps.manifestService.reset(),this.state.sessionActive=!1,new o(e)}try{t("ssai-live-stream"),this.state.steeredSsaiActive=!0,this.state.ssaiController=this.createSsaiController(i.networkCode),await this.state.ssaiController.startSteered(i,s.assetParameters,s.assetParameterMacros),t("scheduling");const a=r(e);this.deps.adPlayerController.setBreakResumePolicy(s.adInsertionType,e.timebase,!1),this.deps.breakScheduler.updateManifest(a),this.deps.pauseAdController.updateManifest(a),this.state.pauseAdTickInterval=setInterval(()=>this.deps.pauseAdController?.tick(),250),this.markPlaybackStartIfPlaying(),this.deps.startLivePushback(),this.startBreakScheduler(),this.startManifestPolling(),this.deps.diagnose("DA-SESSION-STARTED",`SSAI session active (manifest-steered) for ${s.manifestUrl}`,{manifestUrl:s.manifestUrl}),this.deps.log("SSAI session active (manifest-steered)")}catch(e){const s=e instanceof Error?e:new Error(String(e));throw this.deps.diagnose("DA-SSAI-SESSION-FAILED",s.message),this.state.steeredSsaiActive=!1,this.deps.manifestService.reset(),this.state.sessionActive=!1,s}}createSsaiController(e){return new i(this.deps.player,{gam:this.state.steeredSsaiOptions?.streamActivityMonitorId?{streamActivityMonitorId:this.state.steeredSsaiOptions.streamActivityMonitorId}:{},ssai:{networkCode:e},maxBitrate:this.state.steeredSsaiOptions?.maxBitrate,autoplay:this.state.steeredSsaiOptions?.autoplay,debug:this.deps.debug},{emit:(e,s)=>this.deps.emit(e,s),diagnose:(e,s,t)=>this.deps.diagnose(e,s,t),log:(...e)=>this.deps.log(...e)})}startBreakScheduler(){this.deps.breakScheduler.start((e,s)=>{this.deps.breakLifecycle.onBreakStart(e,s)},e=>!this.deps.adPlayerController.isAdPlaying()&&(this.deps.adPlayerController.preload(e).catch(s=>{this.deps.diagnose("DA-PRELOAD-FAILED",s instanceof Error?s.message:String(s),{breakId:e.id})}),!0),(e,s,t)=>{this.deps.adBreakStatusTracker.onBreakWarning(e,s,t)},e=>{this.deps.breakLifecycle.onBreakAborted(e)})}startManifestPolling(){this.deps.manifestService.startPolling(e=>{if(!this.state.sessionActive)return;const s=this.deps.notedManifestBreakIds(e);this.deps.diagnose("DA-MANIFEST-POLLED","Manifest polled",{breaks:e.breaks.length,newBreakIds:s,pollingIntervalSeconds:this.deps.manifestService.currentPollingIntervalSeconds});const t=this.state.sessionManifest,i=t?h(t,e):null;if(this.state.pendingChannelSwitch||i&&this.deps.breakScheduler.getActiveBreak())return this.state.pendingChannelSwitch=e,void this.deps.log("Channel switch deferred until the active break ends");i?this.switchChannel(e,i):this.applyManifestUpdate(e)},e=>{this.state.sessionActive&&this.deps.diagnose("DA-MANIFEST-POLL-FAILED",e.message)})}applyPendingChannelSwitch(){if(!this.state.sessionActive||!this.state.pendingChannelSwitch)return;const e=this.state.pendingChannelSwitch;this.state.pendingChannelSwitch=null;const s=h(this.state.sessionManifest,e);s?this.switchChannel(e,s):this.applyManifestUpdate(e)}applyManifestUpdate(e){this.diagnoseIgnoredDeliveryModeChange(e);const s=t(e);this.state.steeredSsaiActive?this.state.ssaiController?.setManifestAssetParameters(s.ssai?.assetParameters):this.deps.adPlayerController.setManifestAssetParameters(s.sgaiAssetParameters);const i=this.state.steeredSsaiActive?r(e):e;this.deps.breakScheduler.updateManifest(i),this.deps.pauseAdController.updateManifest(i)}diagnoseIgnoredDeliveryModeChange(e){if(!this.state.sessionDeliveryMode)return;const s=l(e,d(),null,"manifest");s.manifestMode!==this.state.sessionDeliveryMode&&this.deps.diagnose("DA-DELIVERY-MODE-CHANGE-IGNORED",`Polled manifest resolves delivery mode "${s.manifestMode}" but "${this.state.sessionDeliveryMode}" is pinned for this session.`,{polledMode:s.manifestMode,pinnedMode:this.state.sessionDeliveryMode})}async switchChannel(e,s){if(this.state.channelSwitchInFlight)return;const t=this.state.sessionConfig,i=this.state.sessionManifest,a={previousChannelId:s.previousChannelId,channelId:s.channelId,reason:s.reason,manifestUrl:t.manifestUrl};this.deps.diagnose("DA-MANIFEST-CHANNEL-CHANGED",`Manifest channel changed (${s.reason}): ${s.previousChannelId??"none"} -> ${s.channelId??"none"}`,a),this.state.sessionManifest=e,this.state.channelSwitchInFlight=!0;const r=this.state.sessionGeneration,n=this.state.sessionDeliveryMode??"sgai";try{let s=this.resolveSessionDeliveryMode(e,"channel-switch");if(this.deps.cmcdAdReporter.onSessionEnd(),this.deps.cmcdAdReporter.onSessionStart(e),this.deps.lensReporter.endSession(),this.deps.lensReporter.startSession(),"ssai"===s&&"sgai"===n){const a=await this.switchToSteeredSsai(e,t);if(this.isStaleStart(r))return;a||(s="sgai",this.state.sessionDeliveryMode="sgai",await this.applySameModeChannelSwitch(i,e,t,r))}else"sgai"===s&&"ssai"===n?await this.switchToSgai(e,t,r):await this.applySameModeChannelSwitch(i,e,t,r);if(this.isStaleStart(r))return}finally{this.state.channelSwitchInFlight=!1}this.deps.startLivePushback(),this.emitChannelChange({...a,previousDeliveryMode:n,deliveryMode:this.state.sessionDeliveryMode??n})}emitChannelChange(e){this.deps.emit("adchannelchange",{type:"adchannelchange",timestamp:Date.now(),...e})}async applySameModeChannelSwitch(e,s,i,a){const n=this.state.steeredSsaiActive,o=n?r(s):s;if(this.deps.breakScheduler.resetForChannelSwitch(o),this.deps.pauseAdController.updateManifest(o),this.deps.adPlayerController.setBreakResumePolicy(i.adInsertionType,s.timebase,this.deps.continueContentDuringBreak),!n&&c(e,s)){const e=this.deps.adPlayerController.updatedAssetParameters,t=this.deps.adPlayerController.sessionAssetParameterMacros;if(this.deps.adPlayerController.endGamSession(),this.deps.adPlayerController.setSessionAssetParameters(i.assetParameters),this.deps.adPlayerController.setSessionAssetParameterMacros(t),await this.startGamForManifest(s,i.assetParameters,e=>{this.deps.diagnose("DA-SESSION-STARTING",`Channel switch: ${e}`,{step:e,manifestUrl:i.manifestUrl})}),this.isStaleStart(a))return;e&&this.deps.adPlayerController.updateAssetParameters(e)}else if(n){const e=t(s);this.state.ssaiController?.setManifestAssetParameters(e.ssai?.assetParameters)}else{const e=t(s);this.deps.adPlayerController.setManifestAssetParameters(e.sgaiAssetParameters)}}async switchToSteeredSsai(e,s){const t=n(e.vendorConfiguration);if(!t)return this.deps.diagnose("DA-SSAI-UNAVAILABLE","Channel switch resolved ssai delivery but no vendor configuration provides a stitched stream; the session stays in sgai.",{manifestUrl:s.manifestUrl,previousMode:"sgai"}),!1;const i=this.deps.adPlayerController,a=i.updatedAssetParameters,o=i.sessionAssetParameterMacros;i.stopActiveBreak(),i.releaseContentHold(),i.endGamSession(),i.setSessionAssetParameters(s.assetParameters),i.setSessionAssetParameterMacros(o),a&&i.updateAssetParameters(a);const d=this.createSsaiController(t.networkCode);this.state.ssaiController=d,this.state.steeredSsaiActive=!0,this.deps.diagnose("DA-SESSION-STARTING","Channel switch: ssai-live-stream",{step:"ssai-live-stream",manifestUrl:s.manifestUrl});try{await d.startSteered(t,s.assetParameters,o),a&&d.updateAssetParameters(a);const n=r(e);return this.deps.breakScheduler.resetForChannelSwitch(n),this.deps.pauseAdController.updateManifest(n),i.setBreakResumePolicy(s.adInsertionType,e.timebase,!1),!0}catch(e){const s=e instanceof Error?e.message:String(e);return this.deps.diagnose("DA-SSAI-SESSION-FAILED",s),d.stop(),delete this.state.ssaiController,this.state.steeredSsaiActive=!1,!1}}async switchToSgai(e,s,t){const i=this.state.ssaiController?.sessionAssetParameterMacros;this.state.ssaiController?.stop(),delete this.state.ssaiController,this.state.steeredSsaiActive=!1,this.deps.breakScheduler.resetForChannelSwitch(e),this.deps.pauseAdController.updateManifest(e);const a=this.deps.adPlayerController;a.setBreakResumePolicy(s.adInsertionType,e.timebase,this.deps.continueContentDuringBreak);const r=a.updatedAssetParameters;a.endGamSession(),a.setSessionAssetParameters(s.assetParameters),a.setSessionAssetParameterMacros(i??s.assetParameterMacros),await this.startGamForManifest(e,s.assetParameters,e=>{this.deps.diagnose("DA-SESSION-STARTING",`Channel switch: ${e}`,{step:e,manifestUrl:s.manifestUrl})}),this.isStaleStart(t)||r&&a.updateAssetParameters(r)}async startGamForManifest(e,i,a){const r=t(e);if(this.deps.adPlayerController.setManifestAssetParameters(r.sgaiAssetParameters),r.unknownTypes.length>0&&this.deps.diagnose("DA-VENDOR-CONFIG-UNKNOWN-TYPE",`Ignored ${r.unknownTypes.length} vendorConfiguration `+(1===r.unknownTypes.length?"entry":"entries")+" with unrecognised type: "+`${r.unknownTypes.join(", ")}.`,{types:r.unknownTypes}),this.deps.adPlayerController.isGamEnabled())if(r.identity){this.deps.diagnose("DA-GAM-CONFIG-RESOLVED",`GAM session identity: network ${r.identity.networkCode}, custom asset ${r.identity.customAssetKey}`,{networkCode:r.identity.networkCode,customAssetKey:r.identity.customAssetKey,channelId:e.channelId,duplicateEntriesIgnored:r.duplicateGam});try{a("gam-init"),await this.deps.adPlayerController.startGamSession(r.identity,i),this.deps.log("GAM session started")}catch(e){this.deps.diagnose("DA-GAM-SESSION-FAILED",e instanceof Error?e.message:String(e))}}else s(e)&&this.deps.diagnose("DA-GAM-CONFIG-MISSING",'The break manifest carries GAM pod assets but no usable "gam" vendorConfiguration entry; those assets will be skipped.',{reason:r.reason});else s(e)&&this.deps.diagnose("DA-GAM-CONFIG-MISSING","The break manifest carries GAM pod assets but GAM is disabled for this session (gam.enabled === false); those assets will be skipped.",{reason:"gam-disabled"})}async startSsaiSession(e,s){try{if(this.state.stitcherOrigin=a(e.stitcherUrl),this.state.configSummary.gam&&(this.state.configSummary.gam={...this.state.configSummary.gam,hasAssetParameters:!!e.assetParameters}),await this.state.ssaiController.start(e),this.isStaleStart(s))return;this.deps.diagnose("DA-SESSION-STARTED",`SSAI session active for ${e.stitcherUrl}`,{stitcherUrl:e.stitcherUrl}),this.deps.startLivePushback(),this.deps.log("SSAI session active")}catch(e){if(this.isStaleStart(s))return;const t=e instanceof Error?e:new Error(String(e));throw this.deps.diagnose("DA-SSAI-SESSION-FAILED",t.message),this.state.sessionActive=!1,this.deps.cmcdAdReporter.onSessionEnd(),this.deps.lensReporter.endSession(),t}}isStaleStart(e){return e!==this.state.sessionGeneration&&(this.deps.log("Session start aborted — the session ended or was replaced while starting"),!0)}}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { KnownDiagnosticCode } from '../diagnostics/errorCodes';
|
|
2
|
+
/**
|
|
3
|
+
* An ad failure classified with its canonical `DA-*` code, which telemetry reports as CMCD
|
|
4
|
+
* `adec`. Failures that are not an `AdError` report the generic `DA-AD-PLAYBACK-ERROR`.
|
|
5
|
+
*/
|
|
6
|
+
export declare class AdError extends Error {
|
|
7
|
+
/** Canonical Ads SDK error code for this failure. */
|
|
8
|
+
readonly code: KnownDiagnosticCode;
|
|
9
|
+
constructor(message: string, code: KnownDiagnosticCode);
|
|
10
|
+
}
|
|
11
|
+
/** Canonical code of an ad failure; `DA-AD-PLAYBACK-ERROR` when it was not classified. */
|
|
12
|
+
export declare function adErrorCode(error: unknown): KnownDiagnosticCode;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export class AdError extends Error{constructor(r,o){super(r),this.name="AdError",this.code=o}}export function adErrorCode(r){return r instanceof AdError?r.code:"DA-AD-PLAYBACK-ERROR"}
|
|
@@ -109,6 +109,18 @@ export type AdInsertionMode = 'overlay' | 'shared-element' | 'adaptive' | 'auto'
|
|
|
109
109
|
* is not consumed. Primarily for VOD (PTS timebase).
|
|
110
110
|
* @category SDK & Sessions */
|
|
111
111
|
export type AdInsertionType = 'replacement' | 'insertion';
|
|
112
|
+
/** Initial live-playback distance from the reported edge. @category Configuration */
|
|
113
|
+
export interface LiveStartPushbackConfig {
|
|
114
|
+
/**
|
|
115
|
+
* Apply once per session/channel. When omitted, it follows `ptsSource`: on for `'anvatoCue'`,
|
|
116
|
+
* off for `'mediaTime'`. An explicit value always wins.
|
|
117
|
+
*/
|
|
118
|
+
enabled?: boolean;
|
|
119
|
+
/** Minimum total distance in target durations, not an additional rewind. @default 5 */
|
|
120
|
+
minimumDistanceTargetDurations?: number;
|
|
121
|
+
/** Declared HLS target duration in seconds when the adapter cannot provide it. */
|
|
122
|
+
targetDurationSeconds?: number;
|
|
123
|
+
}
|
|
112
124
|
/**
|
|
113
125
|
* - `sgai` (default): the SDK polls the break manifest, schedules breaks and plays ads in its own overlay.
|
|
114
126
|
* - `ssai`: the content player plays a pre-stitched stream; the SDK only creates the IMA DAI
|
|
@@ -335,6 +347,11 @@ export interface OptiViewAdsConfig {
|
|
|
335
347
|
* @default false
|
|
336
348
|
*/
|
|
337
349
|
continueContentDuringBreak?: boolean;
|
|
350
|
+
/**
|
|
351
|
+
* Live startup adjustment, on by default with `ptsSource: 'anvatoCue'`; does not change later
|
|
352
|
+
* break-resume policy.
|
|
353
|
+
*/
|
|
354
|
+
liveStartPushback?: LiveStartPushbackConfig;
|
|
338
355
|
/** Hook on the parsed manifest before scheduling (initial fetch and every poll). See {@link ManifestResponseInterceptor}. */
|
|
339
356
|
interceptManifestResponse?: ManifestResponseInterceptor;
|
|
340
357
|
/**
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
+
import type { KnownDiagnosticCode } from '../diagnostics/errorCodes';
|
|
2
|
+
import { AdError } from './AdError';
|
|
1
3
|
/**
|
|
2
4
|
* An ad playback error that carries the ad vendor's own error code (e.g. the
|
|
3
5
|
* numeric IMA/VAST `AdError` code), so telemetry can report it (`adevc`)
|
|
4
|
-
* alongside the SDK's
|
|
5
|
-
* this type when the vendor supplies a code
|
|
6
|
-
* `Error`s.
|
|
6
|
+
* alongside the SDK's canonical code (`adec`). Vendor integrations wrap their
|
|
7
|
+
* errors in this type when the vendor supplies a code.
|
|
7
8
|
*/
|
|
8
|
-
export declare class VendorAdError extends
|
|
9
|
+
export declare class VendorAdError extends AdError {
|
|
9
10
|
/** The vendor's error code, stringified (e.g. IMA's numeric AdError code). */
|
|
10
11
|
readonly vendorCode: string;
|
|
11
|
-
constructor(message: string, vendorCode: string);
|
|
12
|
+
constructor(message: string, vendorCode: string, code?: KnownDiagnosticCode);
|
|
12
13
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export class VendorAdError extends
|
|
1
|
+
import{AdError as r}from"./AdError";export class VendorAdError extends r{constructor(r,o,e="DA-AD-PLAYBACK-ERROR"){super(r,e),this.name="VendorAdError",this.vendorCode=o}}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { Lens } from './lens';
|
|
2
2
|
export { LensEventType, LensToken } from './types';
|
|
3
|
-
export type { LensAdPayload, LensConsent, LensBackend, LensConfigureOptions, LensDestination, LensDestinationFilter, LensEvent, LensEventKeys, LensHandle, LensJoinOptions, LensPayload, LensSession, LensStartSessionOptions, } from './types';
|
|
3
|
+
export type { LensAdPayload, LensBatch, LensConsent, LensBackend, LensConfigureOptions, LensDestination, LensDestinationFilter, LensEvent, LensEventKeys, LensHandle, LensJoinOptions, LensPayload, LensSession, LensSourceChange, LensStartSessionOptions, } from './types';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export{L as Lens,a as LensEventType,b as LensToken}from"./lens-core-
|
|
1
|
+
export{L as Lens,a as LensEventType,b as LensToken}from"./lens-core-uGZfZpQy.js";
|