1- import { createUrl } from "./lib/url" ;
2- import { getAssetsFromVast } from "./vast" ;
3- import type { DateRange } from "./parser" ;
1+ import { DateTime } from "luxon" ;
2+ import { HashGroup } from "./lib/hash-group" ;
3+ import { createUrl , replaceUrlParams } from "./lib/url" ;
4+ import { getAssetsFromVastData , getAssetsFromVastUrl } from "./vast" ;
5+ import type { DateRange , Segment } from "./parser" ;
46import type { Session } from "./session" ;
5- import type { Interstitial , InterstitialAsset } from "./types" ;
6- import type { DateTime } from "luxon" ;
7+ import type { Asset } from "./types" ;
78
8- export function getStaticDateRanges ( session : Session , isLive : boolean ) {
9- return session . interstitials . map < DateRange > ( ( interstitial ) => {
10- const startDate = interstitial . dateTime ;
11- const assetListUrl = getAssetListUrl ( interstitial , session ) ;
9+ // An item describes what we'd like to collect for a particular date.
10+ interface DateGroupItem {
11+ timelineStyle : "HIGHLIGHT" | "PRIMARY" ;
12+ replaceContent : boolean ;
13+ maxDuration ?: number ;
14+ }
15+
16+ export function getStaticDateRanges (
17+ session : Session ,
18+ segments : Segment [ ] ,
19+ isLive : boolean ,
20+ ) {
21+ const dateGroup = new HashGroup < number , DateGroupItem > ( {
22+ getDefaultValue : ( ) => ( {
23+ timelineStyle : "PRIMARY" ,
24+ replaceContent : isLive ? true : false ,
25+ } ) ,
26+ } ) ;
27+
28+ // Collect dateRanges from splice points.
29+ for ( const segment of segments ) {
30+ if ( segment . spliceInfo ?. type !== "OUT" || ! segment . programDateTime ) {
31+ continue ;
32+ }
33+
34+ const key = segment . programDateTime . toMillis ( ) ;
35+ const item = dateGroup . get ( key ) ;
36+
37+ item . timelineStyle = "HIGHLIGHT" ;
38+ item . maxDuration = segment . spliceInfo . duration ;
39+ }
40+
41+ // Collect dateRanges from each event defined in the session.
42+ for ( const event of session . events ) {
43+ const key = event . dateTime . toMillis ( ) ;
44+ const item = dateGroup . get ( key ) ;
45+
46+ if ( event . vast ) {
47+ // If we resolved the event by a vast, we know it's an ad and can mark it
48+ // as HIGHLIGHT on the timeline.
49+ item . timelineStyle = "HIGHLIGHT" ;
50+ }
51+
52+ if (
53+ ! event . maxDuration ||
54+ ( item . maxDuration && event . maxDuration > item . maxDuration )
55+ ) {
56+ // If we have a max duration for this event, we'll save it for this interstitial. Always takes the
57+ // largest maxDuration across events.
58+ item . maxDuration = event . maxDuration ;
59+ }
60+ }
61+
62+ return dateGroup . toEntries ( ) . map < DateRange > ( ( [ key , item ] ) => {
63+ const startDate = DateTime . fromMillis ( key ) ;
64+
65+ const assetListUrl = createUrl ( "out/asset-list.json" , {
66+ dt : startDate . toISO ( ) ,
67+ sid : session . id ,
68+ mdur : item . maxDuration ,
69+ } ) ;
1270
1371 const clientAttributes : Record < string , number | string > = {
1472 RESTRICT : "SKIP,JUMP" ,
1573 "ASSET-LIST" : assetListUrl ,
1674 "CONTENT-MAY-VARY" : "YES" ,
1775 "TIMELINE-OCCUPIES" : "POINT" ,
18- "TIMELINE-STYLE" : getTimelineStyle ( interstitial ) ,
76+ "TIMELINE-STYLE" : item . timelineStyle ,
1977 } ;
2078
21- if ( ! isLive ) {
79+ if ( ! item . replaceContent ) {
2280 clientAttributes [ "RESUME-OFFSET" ] = 0 ;
2381 }
2482
25- if ( interstitial . duration ) {
26- clientAttributes [ "PLAYOUT-LIMIT" ] = interstitial . duration ;
83+ if ( item . maxDuration !== undefined ) {
84+ clientAttributes [ "PLAYOUT-LIMIT" ] = item . maxDuration ;
2785 }
2886
29- const cue : string [ ] = [ "ONCE" ] ;
87+ const cue : string [ ] = [ ] ;
3088 if ( startDate . equals ( session . startTime ) ) {
3189 cue . push ( "PRE" ) ;
3290 }
@@ -44,71 +102,53 @@ export function getStaticDateRanges(session: Session, isLive: boolean) {
44102 } ) ;
45103}
46104
47- export async function getAssets ( session : Session , dateTime : DateTime ) {
48- const interstitial = session . interstitials . find ( ( interstitial ) =>
49- interstitial . dateTime . equals ( dateTime ) ,
105+ export async function getAssets (
106+ session : Session ,
107+ dateTime : DateTime ,
108+ maxDuration ?: number ,
109+ ) : Promise < Asset [ ] > {
110+ // Filter all events for a particular dateTime, we'll need to transform these to
111+ // a list of assets.
112+ const events = session . events . filter ( ( event ) =>
113+ event . dateTime . equals ( dateTime ) ,
50114 ) ;
51115
52- if ( ! interstitial ) {
53- return [ ] ;
54- }
55-
56- const assets : InterstitialAsset [ ] = [ ] ;
57-
58- for ( const chunk of interstitial . chunks ) {
59- if ( chunk . type === "vast" ) {
60- const nextAssets = await getAssetsFromVast ( chunk . data ) ;
61- assets . push ( ...nextAssets ) ;
62- }
63- if ( chunk . type === "asset" ) {
64- assets . push ( chunk . data ) ;
116+ const assets : Asset [ ] = [ ] ;
117+
118+ for ( const event of events ) {
119+ if ( event . vast ) {
120+ const { url, data } = event . vast ;
121+
122+ // The event contains a VAST url.
123+ if ( url ) {
124+ const vastUrl = replaceUrlParams ( url , {
125+ maxDuration,
126+ } ) ;
127+ const vastAssets = await getAssetsFromVastUrl ( vastUrl ) ;
128+ assets . push ( ...vastAssets ) ;
129+ }
130+
131+ // The event contains inline VAST data.
132+ if ( data ) {
133+ const vastAssets = await getAssetsFromVastData ( data ) ;
134+ assets . push ( ...vastAssets ) ;
135+ }
65136 }
66- }
67137
68- return assets ;
69- }
70-
71- export function mergeInterstitials (
72- source : Interstitial [ ] ,
73- interstitials : Interstitial [ ] ,
74- ) {
75- for ( const interstitial of interstitials ) {
76- const target = source . find ( ( item ) =>
77- item . dateTime . equals ( interstitial . dateTime ) ,
78- ) ;
79-
80- if ( ! target ) {
81- source . push ( interstitial ) ;
82- } else {
83- // If we found a source for the particular dateTime, we push the
84- // other chunks at the end.
85- target . chunks . push ( ...interstitial . chunks ) ;
138+ // The event contains a list of assets, explicitly defined.
139+ if ( event . assets ) {
140+ assets . push ( ...event . assets ) ;
86141 }
87142 }
88- }
89143
90- function getAssetListUrl ( interstitial : Interstitial , session ?: Session ) {
91- const assetListChunks = interstitial . chunks . filter (
92- ( chunk ) => chunk . type === "assetList" ,
93- ) ;
94- if ( assetListChunks . length === 1 && assetListChunks [ 0 ] ) {
95- return assetListChunks [ 0 ] . data . url ;
144+ // If we have a generic vast config on our session, use that one to resolve (eg; for live streams)
145+ if ( session . vast ?. url ) {
146+ const vastUrl = replaceUrlParams ( session . vast . url , {
147+ maxDuration,
148+ } ) ;
149+ const tempAssets = await getAssetsFromVastUrl ( vastUrl ) ;
150+ assets . push ( ...tempAssets ) ;
96151 }
97152
98- return createUrl ( "out/asset-list.json" , {
99- dt : interstitial . dateTime . toISO ( ) ,
100- sid : session ?. id ,
101- } ) ;
102- }
103-
104- function getTimelineStyle ( interstitial : Interstitial ) {
105- for ( const chunk of interstitial . chunks ) {
106- if ( chunk . type === "asset" && chunk . data . kind === "ad" ) {
107- return "HIGHLIGHT" ;
108- }
109- if ( chunk . type === "vast" ) {
110- return "HIGHLIGHT" ;
111- }
112- }
113- return "PRIMARY" ;
153+ return assets ;
114154}
0 commit comments