|
1 | 1 | import { createUrl } from "./lib/url"; |
2 | | -import { fetchDuration } from "./playlist"; |
3 | | -import { getAdMediasFromVast } from "./vast"; |
| 2 | +import { getAssetsFromVast } from "./vast"; |
| 3 | +import type { DateRange } from "./parser"; |
4 | 4 | import type { Session } from "./session"; |
5 | | -import type { Interstitial, InterstitialAssetType } from "./types"; |
| 5 | +import type { Interstitial, InterstitialAsset } from "./types"; |
6 | 6 | import type { DateTime } from "luxon"; |
7 | 7 |
|
8 | 8 | export function getStaticDateRanges(session: Session, isLive: boolean) { |
9 | | - const group: { |
10 | | - dateTime: DateTime; |
11 | | - types: InterstitialAssetType[]; |
12 | | - }[] = []; |
13 | | - |
14 | | - for (const interstitial of session.interstitials) { |
15 | | - let item = group.find((item) => |
16 | | - item.dateTime.equals(interstitial.dateTime), |
17 | | - ); |
18 | | - |
19 | | - if (!item) { |
20 | | - item = { |
21 | | - dateTime: interstitial.dateTime, |
22 | | - types: [], |
23 | | - }; |
24 | | - group.push(item); |
25 | | - } |
26 | | - |
27 | | - const type = getInterstitialType(interstitial); |
28 | | - if (type && !item.types.includes(type)) { |
29 | | - item.types.push(type); |
30 | | - } |
31 | | - } |
32 | | - |
33 | | - return group.map((item) => { |
34 | | - const assetListUrl = createAssetListUrl({ |
35 | | - dateTime: item.dateTime, |
36 | | - session, |
37 | | - }); |
| 9 | + return session.interstitials.map<DateRange>((interstitial) => { |
| 10 | + const startDate = interstitial.dateTime; |
| 11 | + const assetListUrl = getAssetListUrl(interstitial, session); |
38 | 12 |
|
39 | 13 | const clientAttributes: Record<string, number | string> = { |
40 | 14 | RESTRICT: "SKIP,JUMP", |
41 | 15 | "ASSET-LIST": assetListUrl, |
42 | | - CUE: "ONCE", |
| 16 | + "CONTENT-MAY-VARY": "YES", |
| 17 | + "TIMELINE-OCCUPIES": "POINT", |
| 18 | + "TIMELINE-STYLE": getTimelineStyle(interstitial), |
43 | 19 | }; |
44 | 20 |
|
45 | 21 | if (!isLive) { |
46 | 22 | clientAttributes["RESUME-OFFSET"] = 0; |
47 | 23 | } |
48 | 24 |
|
49 | | - const isPreroll = item.dateTime.equals(session.startTime); |
50 | | - if (isPreroll) { |
51 | | - clientAttributes["CUE"] += ",PRE"; |
| 25 | + const cue: string[] = ["ONCE"]; |
| 26 | + if (startDate.equals(session.startTime)) { |
| 27 | + cue.push("PRE"); |
52 | 28 | } |
53 | 29 |
|
54 | | - if (item.types.length) { |
55 | | - clientAttributes["SPRS-TYPES"] = item.types.join(","); |
| 30 | + if (cue.length) { |
| 31 | + clientAttributes["CUE"] = cue.join(","); |
56 | 32 | } |
57 | 33 |
|
58 | 34 | return { |
59 | 35 | classId: "com.apple.hls.interstitial", |
60 | | - id: `${item.dateTime.toUnixInteger()}`, |
61 | | - startDate: item.dateTime, |
| 36 | + id: `${startDate.toMillis()}`, |
| 37 | + startDate, |
62 | 38 | clientAttributes, |
63 | 39 | }; |
64 | 40 | }); |
65 | 41 | } |
66 | 42 |
|
67 | 43 | export async function getAssets(session: Session, dateTime: DateTime) { |
68 | | - const assets: { |
69 | | - URI: string; |
70 | | - DURATION: number; |
71 | | - "SPRS-TYPE"?: InterstitialAssetType; |
72 | | - }[] = []; |
| 44 | + const assets: InterstitialAsset[] = []; |
73 | 45 |
|
74 | | - const interstitials = session.interstitials.filter((interstitial) => |
| 46 | + const interstitial = session.interstitials.find((interstitial) => |
75 | 47 | interstitial.dateTime.equals(dateTime), |
76 | 48 | ); |
77 | 49 |
|
| 50 | + if (interstitial?.vast) { |
| 51 | + const nextAssets = await getAssetsFromVast(interstitial.vast); |
| 52 | + assets.push(...nextAssets); |
| 53 | + } |
| 54 | + |
| 55 | + if (interstitial?.assets) { |
| 56 | + assets.push(...interstitial.assets); |
| 57 | + } |
| 58 | + |
| 59 | + return assets; |
| 60 | +} |
| 61 | + |
| 62 | +export function appendInterstitials( |
| 63 | + source: Interstitial[], |
| 64 | + interstitials: Interstitial[], |
| 65 | +) { |
78 | 66 | for (const interstitial of interstitials) { |
79 | | - const adMedias = await getAdMediasFromVast(interstitial); |
80 | | - for (const adMedia of adMedias) { |
81 | | - assets.push({ |
82 | | - URI: adMedia.masterUrl, |
83 | | - DURATION: adMedia.duration, |
84 | | - "SPRS-TYPE": "ad", |
85 | | - }); |
| 67 | + const target = source.find((item) => |
| 68 | + item.dateTime.equals(interstitial.dateTime), |
| 69 | + ); |
| 70 | + |
| 71 | + if (!target) { |
| 72 | + source.push(interstitial); |
| 73 | + continue; |
86 | 74 | } |
87 | 75 |
|
88 | | - if (interstitial.asset) { |
89 | | - assets.push({ |
90 | | - URI: interstitial.asset.url, |
91 | | - DURATION: await fetchDuration(interstitial.asset.url), |
92 | | - "SPRS-TYPE": interstitial.asset.type, |
93 | | - }); |
| 76 | + if (interstitial.assets) { |
| 77 | + if (!target.assets) { |
| 78 | + target.assets = interstitial.assets; |
| 79 | + } else { |
| 80 | + target.assets.push(...interstitial.assets); |
| 81 | + } |
94 | 82 | } |
95 | | - } |
96 | 83 |
|
97 | | - return assets; |
| 84 | + if (interstitial.vast) { |
| 85 | + target.vast = interstitial.vast; |
| 86 | + } |
| 87 | + |
| 88 | + if (interstitial.assetList) { |
| 89 | + target.assetList = interstitial.assetList; |
| 90 | + } |
| 91 | + } |
98 | 92 | } |
99 | 93 |
|
100 | | -function createAssetListUrl(params: { dateTime: DateTime; session?: Session }) { |
| 94 | +function getAssetListUrl(interstitial: Interstitial, session?: Session) { |
| 95 | + if (interstitial.assetList) { |
| 96 | + return interstitial.assetList.url; |
| 97 | + } |
101 | 98 | return createUrl("out/asset-list.json", { |
102 | | - dt: params.dateTime.toISO(), |
103 | | - sid: params.session?.id, |
| 99 | + dt: interstitial.dateTime.toISO(), |
| 100 | + sid: session?.id, |
104 | 101 | }); |
105 | 102 | } |
106 | 103 |
|
107 | | -function getInterstitialType( |
108 | | - interstitial: Interstitial, |
109 | | -): InterstitialAssetType | undefined { |
110 | | - if (interstitial.vastData || interstitial.vastUrl) { |
111 | | - return "ad"; |
| 104 | +function getTimelineStyle(interstitial: Interstitial) { |
| 105 | + if (interstitial.assets) { |
| 106 | + for (const asset of interstitial.assets) { |
| 107 | + if (asset.kind === "ad") { |
| 108 | + return "HIGHLIGHT"; |
| 109 | + } |
| 110 | + } |
112 | 111 | } |
113 | | - return interstitial.asset?.type; |
| 112 | + |
| 113 | + if (interstitial.vast) { |
| 114 | + return "HIGHLIGHT"; |
| 115 | + } |
| 116 | + |
| 117 | + return "PRIMARY"; |
114 | 118 | } |
0 commit comments