1
- import { DateTime } from "luxon" ;
2
1
import { assert } from "shared/assert" ;
3
- import { env } from "./env " ;
4
- import { resolveUri , toAssetProtocol } from "./lib/url" ;
5
- import { fetchMasterPlaylistDuration } from "./playlist" ;
2
+ import { Group } from "./lib/group " ;
3
+ import { buildProxyUrl , resolveUri } from "./lib/url" ;
4
+ import { fetchDuration } from "./playlist" ;
6
5
import { getAdMediasFromAdBreak } from "./vast" ;
7
6
import { parseVmap } from "./vmap" ;
8
7
import type { DateRange } from "./parser" ;
9
8
import type { Session } from "./session" ;
10
9
import type { VmapResponse } from "./vmap" ;
10
+ import type { DateTime } from "luxon" ;
11
11
12
12
export type InterstitialType = "ad" | "bumper" ;
13
13
14
14
export interface Interstitial {
15
- timeOffset : number ;
16
15
url : string ;
16
+ duration ?: number ;
17
17
type ?: InterstitialType ;
18
18
}
19
19
@@ -23,30 +23,33 @@ interface InterstitialAsset {
23
23
"SPRS-TYPE" ?: InterstitialType ;
24
24
}
25
25
26
- export function getStaticDateRanges ( session : Session ) {
27
- assert ( session . startTime , "No startTime in session" ) ;
28
-
29
- const group : Record < string , InterstitialType [ ] > = { } ;
26
+ export function getStaticDateRanges ( startTime : DateTime , session : Session ) {
27
+ const group = new Group < number , InterstitialType | undefined > ( ) ;
30
28
31
29
if ( session . vmapResponse ) {
32
30
const vmap = parseVmap ( session . vmapResponse ) ;
33
31
for ( const adBreak of vmap . adBreaks ) {
34
- const dateTime = session . startTime . plus ( { seconds : adBreak . timeOffset } ) ;
35
- groupTimeOffset ( group , dateTime , "ad" ) ;
32
+ group . add ( adBreak . timeOffset , "ad" ) ;
36
33
}
37
34
}
38
35
39
- if ( session . interstitials ) {
40
- for ( const interstitial of session . interstitials ) {
41
- const dateTime = session . startTime . plus ( {
42
- seconds : interstitial . timeOffset ,
43
- } ) ;
44
- groupTimeOffset ( group , dateTime , interstitial . type ) ;
45
- }
46
- }
36
+ session . interstitials ?. forEach ( ( timeOffset , interstitials ) => {
37
+ interstitials . forEach ( ( interstitial ) => {
38
+ group . add ( timeOffset , interstitial . type ) ;
39
+ } ) ;
40
+ } ) ;
47
41
48
- return Object . entries ( group ) . map < DateRange > ( ( [ startDate , types ] , index ) => {
49
- const assetListUrl = `${ env . PUBLIC_STITCHER_ENDPOINT } /session/${ session . id } /asset-list.json?startDate=${ encodeURIComponent ( startDate ) } ` ;
42
+ const dateRanges : DateRange [ ] = [ ] ;
43
+
44
+ group . forEach ( ( timeOffset , types ) => {
45
+ const startDate = startTime . plus ( { seconds : timeOffset } ) ;
46
+
47
+ const assetListUrl = buildProxyUrl (
48
+ `session/${ session . id } /asset-list.json` ,
49
+ {
50
+ startDate : startDate . toISO ( ) ,
51
+ } ,
52
+ ) ;
50
53
51
54
const clientAttributes : Record < string , number | string > = {
52
55
RESTRICT : "SKIP,JUMP" ,
@@ -58,106 +61,103 @@ export function getStaticDateRanges(session: Session) {
58
61
clientAttributes [ "SPRS-TYPES" ] = types . join ( "," ) ;
59
62
}
60
63
61
- return {
64
+ dateRanges . push ( {
62
65
classId : "com.apple.hls.interstitial" ,
63
- id : `i ${ index } ` ,
64
- startDate : DateTime . fromISO ( startDate ) ,
66
+ id : `sdr ${ timeOffset } ` ,
67
+ startDate,
65
68
clientAttributes,
66
- } ;
69
+ } ) ;
67
70
} ) ;
68
- }
69
71
70
- function groupTimeOffset (
71
- group : Record < string , InterstitialType [ ] > ,
72
- dateTime : DateTime ,
73
- type ?: InterstitialType ,
74
- ) {
75
- const key = dateTime . toISO ( ) ;
76
- if ( ! key ) {
77
- return ;
78
- }
79
- if ( ! group [ key ] ) {
80
- group [ key ] = [ ] ;
81
- }
82
- if ( type ) {
83
- group [ key ] . push ( type ) ;
84
- }
72
+ return dateRanges ;
85
73
}
86
74
87
75
export async function getAssets ( session : Session , lookupDate : DateTime ) {
88
- assert ( session . startTime , "No startTime in session" ) ;
89
-
90
76
const assets : InterstitialAsset [ ] = [ ] ;
91
77
92
- if ( session . vmapResponse ) {
93
- const vmap = parseVmap ( session . vmapResponse ) ;
94
- await formatStaticAdBreaks ( assets , vmap , session . startTime , lookupDate ) ;
95
- }
78
+ if ( session . startTime ) {
79
+ if ( session . vmapResponse ) {
80
+ const vmap = parseVmap ( session . vmapResponse ) ;
81
+ const vmapAssets = await getAssetsFromVmap (
82
+ vmap ,
83
+ session . startTime ,
84
+ lookupDate ,
85
+ ) ;
86
+ assets . push ( ...vmapAssets ) ;
87
+ }
96
88
97
- if ( session . interstitials ) {
98
- await formatStaticInterstitials (
99
- assets ,
100
- session . interstitials ,
101
- session . startTime ,
102
- lookupDate ,
103
- ) ;
89
+ if ( session . interstitials ) {
90
+ const groupAssets = await getAssetsFromGroup (
91
+ session . interstitials ,
92
+ session . startTime ,
93
+ lookupDate ,
94
+ ) ;
95
+ assets . push ( ...groupAssets ) ;
96
+ }
104
97
}
105
98
106
99
return assets ;
107
100
}
108
101
109
- async function formatStaticAdBreaks (
110
- assets : InterstitialAsset [ ] ,
102
+ async function getAssetsFromVmap (
111
103
vmap : VmapResponse ,
112
104
baseDate : DateTime ,
113
105
lookupDate : DateTime ,
114
106
) {
115
- const adBreak = vmap . adBreaks . find ( ( adBreak ) =>
116
- isEqualTimeOffset ( baseDate , adBreak . timeOffset , lookupDate ) ,
107
+ const timeOffset = getTimeOffset ( baseDate , lookupDate ) ;
108
+ const adBreak = vmap . adBreaks . find (
109
+ ( adBreak ) => adBreak . timeOffset === timeOffset ,
117
110
) ;
118
111
119
112
if ( ! adBreak ) {
120
113
// No adbreak found for the time offset. There's nothing left to do.
121
- return ;
114
+ return [ ] ;
122
115
}
123
116
117
+ const assets : InterstitialAsset [ ] = [ ] ;
118
+
124
119
const adMedias = await getAdMediasFromAdBreak ( adBreak ) ;
125
120
126
121
for ( const adMedia of adMedias ) {
127
- const uri = toAssetProtocol ( adMedia . assetId ) ;
128
122
assets . push ( {
129
- URI : resolveUri ( uri ) ,
123
+ URI : resolveUri ( `asset:// ${ adMedia . assetId } ` ) ,
130
124
DURATION : adMedia . duration ,
131
125
"SPRS-TYPE" : "ad" ,
132
126
} ) ;
133
127
}
128
+
129
+ return assets ;
134
130
}
135
131
136
- async function formatStaticInterstitials (
137
- assets : InterstitialAsset [ ] ,
138
- interstitials : Interstitial [ ] ,
132
+ async function getAssetsFromGroup (
133
+ interstitialsGroup : Group < number , Interstitial > ,
139
134
baseDate : DateTime ,
140
135
lookupDate : DateTime ,
141
136
) {
142
- // Filter each interstitial and match it with the given lookup time.
143
- const list = interstitials . filter ( ( interstitial ) =>
144
- isEqualTimeOffset ( baseDate , interstitial . timeOffset , lookupDate ) ,
145
- ) ;
137
+ const assets : InterstitialAsset [ ] = [ ] ;
138
+
139
+ const timeOffset = getTimeOffset ( baseDate , lookupDate ) ;
140
+
141
+ const interstitials = interstitialsGroup . get ( timeOffset ) ;
142
+
143
+ for ( const interstitial of interstitials ) {
144
+ let duration = interstitial . duration ;
145
+ if ( ! duration ) {
146
+ duration = await fetchDuration ( interstitial . url ) ;
147
+ }
146
148
147
- for ( const interstitial of list ) {
148
- const duration = await fetchMasterPlaylistDuration ( interstitial . url ) ;
149
149
assets . push ( {
150
150
URI : interstitial . url ,
151
151
DURATION : duration ,
152
152
"SPRS-TYPE" : interstitial . type ,
153
153
} ) ;
154
154
}
155
+
156
+ return assets ;
155
157
}
156
158
157
- function isEqualTimeOffset (
158
- baseDate : DateTime ,
159
- timeOffset : number ,
160
- lookupDate : DateTime ,
161
- ) {
162
- return baseDate . plus ( { seconds : timeOffset } ) . toISO ( ) === lookupDate . toISO ( ) ;
159
+ function getTimeOffset ( baseDate : DateTime , lookupDate : DateTime ) {
160
+ const { seconds } = lookupDate . diff ( baseDate , "seconds" ) . toObject ( ) ;
161
+ assert ( seconds ) ;
162
+ return seconds ;
163
163
}
0 commit comments