From aea2d1cbf51963c7d1feb8ca12d35ffe4a0917f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabi=C3=A1n=20Cancela?= <62720589+fcancela@users.noreply.github.com> Date: Sat, 7 Dec 2024 17:07:44 -0300 Subject: [PATCH 1/4] feat: Add Docker Compose for Development Environment (#132) * Create a docker-compose-dev.yml for quicker setup * remove unnecesary script * Use suggested bun naming convention for dev env files * Add Cors Policy to bucket upon creation * change dev compose postgres default password for dev environment * Uses NODE_ENV in package.json and setup environment variables based on NODE_ENV * Fix postgres pw mismatch between docker-compose and config.env.development * Ignore config.env.development to avoid users pushing sensitive information. Added sample config.env.development to getting-started guide * Improve getting started guide --- .gitignore | 6 +++- bun.lockb | Bin 508280 -> 509560 bytes docker/aws/buckets.sh | 8 +++++ docker/aws/cors.json | 10 ++++++ docker/docker-compose-dev.yml | 45 ++++++++++++++++++++++++++ docs/guide/getting-started.md | 56 +++++++++++++++++++++++++++++++-- docs/guide/video-processing.md | 13 ++++---- package.json | 4 +-- packages/shared/src/env.ts | 15 ++++++++- 9 files changed, 144 insertions(+), 13 deletions(-) create mode 100755 docker/aws/buckets.sh create mode 100644 docker/aws/cors.json create mode 100644 docker/docker-compose-dev.yml diff --git a/.gitignore b/.gitignore index edb78ee4..45a8b908 100644 --- a/.gitignore +++ b/.gitignore @@ -36,4 +36,8 @@ vite.config.ts.timestamp* config.env* !config.env.example -.wrangler \ No newline at end of file +.wrangler + +# Dev Folder +docker/volume +docker/sprs-localstack-data diff --git a/bun.lockb b/bun.lockb index 17fac815aaa2e2341267d02a2e59302c78291e66..e27253acc6bd62f6f51a63cf3d681bd12423739e 100755 GIT binary patch delta 100267 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%0A6RaqQ3wJ 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 X#gkzN^oK*`y)SFUlWf5O{l@ = (parser: typeof z) => S; From 1715d0031ab0ef0c3d3161c8ce6cc21980e148f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabi=C3=A1n=20Cancela?= <62720589+fcancela@users.noreply.github.com> Date: Mon, 9 Dec 2024 17:41:33 -0300 Subject: [PATCH 2/4] docs: Fixes Getting Started Markdown Extension Usage in Quick Development Environment Setup Section (#142) --- docs/guide/getting-started.md | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/docs/guide/getting-started.md b/docs/guide/getting-started.md index 680cf5c9..cf470d92 100644 --- a/docs/guide/getting-started.md +++ b/docs/guide/getting-started.md @@ -234,19 +234,27 @@ We've already covered how to build Superstreamer locally, and we've also made it $ bun run dev ``` +::: + ### Quick Development Environment Setup We have also created a `docker-compose-dev.yml` so you can setup your development environment faster and start getting hands on! -```shell +::: code-group + +```sh [Terminal] # We have prebuilt development containers, see docker/docker-compose-dev.yml cd docker docker-compose -f docker-compose-dev.yml up ``` +::: + You can create a file named `config.env.development` for a quick setup. Here is a sample that should work out of the box if default configuration is used: -```shell +::: code-group + +```sh [config.env.development] S3_ENDPOINT=http://s3.localhost.localstack.cloud:4566/ S3_REGION=us-east-1 S3_ACCESS_KEY=test @@ -272,9 +280,13 @@ SUPER_SECRET=abc DATABASE_URI=postgresql://postgres:sprs@localhost:5432/sprs ``` +::: + Run it with: -```shell +::: code-group + +```sh [Terminal] # Install dependencies bun install From b2dcca30690c2016ec5c841ac48b4bd5e6529a8d Mon Sep 17 00:00:00 2001 From: Matthias Van Parijs Date: Mon, 9 Dec 2024 23:11:18 +0100 Subject: [PATCH 3/4] chore: Added a bunch of readme --- packages/api/README.md | 8 ++++++++ packages/app/README.md | 3 +++ packages/artisan/README.md | 5 +++++ packages/bolt/README.md | 3 +++ packages/stitcher/README.md | 6 ++++++ 5 files changed, 25 insertions(+) create mode 100644 packages/api/README.md create mode 100644 packages/app/README.md create mode 100644 packages/artisan/README.md create mode 100644 packages/bolt/README.md create mode 100644 packages/stitcher/README.md diff --git a/packages/api/README.md b/packages/api/README.md new file mode 100644 index 00000000..994b4028 --- /dev/null +++ b/packages/api/README.md @@ -0,0 +1,8 @@ +# @superstreamer/api + +The main API for: + +- Asset management. +- Start transcode, package (or pipeline) jobs. +- Get a job by id, read status. +- Get storage info, such as transcode results. \ No newline at end of file diff --git a/packages/app/README.md b/packages/app/README.md new file mode 100644 index 00000000..547d7166 --- /dev/null +++ b/packages/app/README.md @@ -0,0 +1,3 @@ +# @superstreamer/app + +The app serves as a user-friendly dashboard, enabling interaction with Superstreamer through its API. \ No newline at end of file diff --git a/packages/artisan/README.md b/packages/artisan/README.md new file mode 100644 index 00000000..a1282922 --- /dev/null +++ b/packages/artisan/README.md @@ -0,0 +1,5 @@ +# @superstreamer/artisan + +The actual workers, this is where ffmpeg, ffprobe and the packager run. + +We rely on [bullmq](https://www.npmjs.com/package/bullmq) as queue. Artisan will pick an available job from the queue and get to work. \ No newline at end of file diff --git a/packages/bolt/README.md b/packages/bolt/README.md new file mode 100644 index 00000000..534e6e51 --- /dev/null +++ b/packages/bolt/README.md @@ -0,0 +1,3 @@ +# bolt + +An **internal** package that manages the different queues. This package is mainly shared between the API (pushing a job to the queue) and Artisan (consuming a job, does the actual work). \ No newline at end of file diff --git a/packages/stitcher/README.md b/packages/stitcher/README.md new file mode 100644 index 00000000..b5167daa --- /dev/null +++ b/packages/stitcher/README.md @@ -0,0 +1,6 @@ +# @superstreamer/stitcher + +A real-time playlist manipulator, able to proxy HLS playlists and insert interstitials on the fly. + +- Filtering of the master playlist. +- Resolves a VMAP or a VAST and adds the ad as part of an interstitial. \ No newline at end of file From 2b593037db668b667e92c91431ab1839bfb8da26 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 10 Dec 2024 19:04:14 +0100 Subject: [PATCH 4/4] feat: Discriminated unions for interstitials type (#139) * Added discriminated unions for interstitials type * Added assetList type * Added timeline style * Added tests * New interstitials API * Fix snapshots --- packages/stitcher/src/interstitials.ts | 144 +++++++-------- packages/stitcher/src/playlist.ts | 50 ++++-- packages/stitcher/src/routes/session.ts | 26 ++- packages/stitcher/src/session.ts | 78 ++++---- packages/stitcher/src/types.ts | 18 +- packages/stitcher/src/vast.ts | 46 +++-- .../__snapshots__/interstitials.test.ts.snap | 170 ++++++++++++++++-- .../test/__snapshots__/playlist.test.ts.snap | 20 ++- packages/stitcher/test/interstitials.test.ts | 52 +++++- packages/stitcher/test/playlist.test.ts | 34 ++-- packages/stitcher/test/setup.ts | 6 + .../stitcher/test/{mock.ts => test-data.ts} | 72 ++++---- 12 files changed, 483 insertions(+), 233 deletions(-) rename packages/stitcher/test/{mock.ts => test-data.ts} (57%) diff --git a/packages/stitcher/src/interstitials.ts b/packages/stitcher/src/interstitials.ts index 68855bef..96ddd258 100644 --- a/packages/stitcher/src/interstitials.ts +++ b/packages/stitcher/src/interstitials.ts @@ -1,114 +1,118 @@ import { createUrl } from "./lib/url"; -import { fetchDuration } from "./playlist"; -import { getAdMediasFromVast } from "./vast"; +import { getAssetsFromVast } from "./vast"; +import type { DateRange } from "./parser"; import type { Session } from "./session"; -import type { Interstitial, InterstitialAssetType } from "./types"; +import type { Interstitial, InterstitialAsset } from "./types"; import type { DateTime } from "luxon"; export function getStaticDateRanges(session: Session, isLive: boolean) { - const group: { - dateTime: DateTime; - types: InterstitialAssetType[]; - }[] = []; - - for (const interstitial of session.interstitials) { - let item = group.find((item) => - item.dateTime.equals(interstitial.dateTime), - ); - - if (!item) { - item = { - dateTime: interstitial.dateTime, - types: [], - }; - group.push(item); - } - - const type = getInterstitialType(interstitial); - if (type && !item.types.includes(type)) { - item.types.push(type); - } - } - - return group.map((item) => { - const assetListUrl = createAssetListUrl({ - dateTime: item.dateTime, - session, - }); + return session.interstitials.map((interstitial) => { + const startDate = interstitial.dateTime; + const assetListUrl = getAssetListUrl(interstitial, session); const clientAttributes: Record = { RESTRICT: "SKIP,JUMP", "ASSET-LIST": assetListUrl, - CUE: "ONCE", + "CONTENT-MAY-VARY": "YES", + "TIMELINE-OCCUPIES": "POINT", + "TIMELINE-STYLE": getTimelineStyle(interstitial), }; if (!isLive) { clientAttributes["RESUME-OFFSET"] = 0; } - const isPreroll = item.dateTime.equals(session.startTime); - if (isPreroll) { - clientAttributes["CUE"] += ",PRE"; + const cue: string[] = ["ONCE"]; + if (startDate.equals(session.startTime)) { + cue.push("PRE"); } - if (item.types.length) { - clientAttributes["SPRS-TYPES"] = item.types.join(","); + if (cue.length) { + clientAttributes["CUE"] = cue.join(","); } return { classId: "com.apple.hls.interstitial", - id: `${item.dateTime.toUnixInteger()}`, - startDate: item.dateTime, + id: `${startDate.toMillis()}`, + startDate, clientAttributes, }; }); } export async function getAssets(session: Session, dateTime: DateTime) { - const assets: { - URI: string; - DURATION: number; - "SPRS-TYPE"?: InterstitialAssetType; - }[] = []; + const assets: InterstitialAsset[] = []; - const interstitials = session.interstitials.filter((interstitial) => + const interstitial = session.interstitials.find((interstitial) => interstitial.dateTime.equals(dateTime), ); + if (interstitial?.vast) { + const nextAssets = await getAssetsFromVast(interstitial.vast); + assets.push(...nextAssets); + } + + if (interstitial?.assets) { + assets.push(...interstitial.assets); + } + + return assets; +} + +export function appendInterstitials( + source: Interstitial[], + interstitials: Interstitial[], +) { for (const interstitial of interstitials) { - const adMedias = await getAdMediasFromVast(interstitial); - for (const adMedia of adMedias) { - assets.push({ - URI: adMedia.masterUrl, - DURATION: adMedia.duration, - "SPRS-TYPE": "ad", - }); + const target = source.find((item) => + item.dateTime.equals(interstitial.dateTime), + ); + + if (!target) { + source.push(interstitial); + continue; } - if (interstitial.asset) { - assets.push({ - URI: interstitial.asset.url, - DURATION: await fetchDuration(interstitial.asset.url), - "SPRS-TYPE": interstitial.asset.type, - }); + if (interstitial.assets) { + if (!target.assets) { + target.assets = interstitial.assets; + } else { + target.assets.push(...interstitial.assets); + } } - } - return assets; + if (interstitial.vast) { + target.vast = interstitial.vast; + } + + if (interstitial.assetList) { + target.assetList = interstitial.assetList; + } + } } -function createAssetListUrl(params: { dateTime: DateTime; session?: Session }) { +function getAssetListUrl(interstitial: Interstitial, session?: Session) { + if (interstitial.assetList) { + return interstitial.assetList.url; + } return createUrl("out/asset-list.json", { - dt: params.dateTime.toISO(), - sid: params.session?.id, + dt: interstitial.dateTime.toISO(), + sid: session?.id, }); } -function getInterstitialType( - interstitial: Interstitial, -): InterstitialAssetType | undefined { - if (interstitial.vastData || interstitial.vastUrl) { - return "ad"; +function getTimelineStyle(interstitial: Interstitial) { + if (interstitial.assets) { + for (const asset of interstitial.assets) { + if (asset.kind === "ad") { + return "HIGHLIGHT"; + } + } } - return interstitial.asset?.type; + + if (interstitial.vast) { + return "HIGHLIGHT"; + } + + return "PRIMARY"; } diff --git a/packages/stitcher/src/playlist.ts b/packages/stitcher/src/playlist.ts index 0f015f83..d3352568 100644 --- a/packages/stitcher/src/playlist.ts +++ b/packages/stitcher/src/playlist.ts @@ -1,6 +1,10 @@ import { assert } from "shared/assert"; import { filterMasterPlaylist, formatFilterToQueryParam } from "./filters"; -import { getAssets, getStaticDateRanges } from "./interstitials"; +import { + appendInterstitials, + getAssets, + getStaticDateRanges, +} from "./interstitials"; import { encrypt } from "./lib/crypto"; import { createUrl, joinUrl, resolveUri } from "./lib/url"; import { @@ -15,6 +19,7 @@ import { fetchVmap, toAdBreakTimeOffset } from "./vmap"; import type { Filter } from "./filters"; import type { MasterPlaylist, MediaPlaylist, RenditionType } from "./parser"; import type { Session } from "./session"; +import type { Interstitial } from "./types"; import type { VmapAdBreak } from "./vmap"; import type { DateTime } from "luxon"; @@ -71,8 +76,17 @@ export async function formatMediaPlaylist( export async function formatAssetList(session: Session, dateTime: DateTime) { const assets = await getAssets(session, dateTime); + + const assetsPromises = assets.map(async (asset) => { + return { + URI: asset.url, + DURATION: asset.duration ?? (await fetchDuration(asset.url)), + "SPRS-KIND": asset.kind, + }; + }); + return { - ASSETS: assets, + ASSETS: await Promise.all(assetsPromises), }; } @@ -181,8 +195,14 @@ async function initSessionOnMasterReq(session: Session) { if (session.vmap) { const vmap = await fetchVmap(session.vmap); + delete session.vmap; - mapAdBreaksToSessionInterstitials(session, vmap.adBreaks); + + const interstitials = mapAdBreaksToSessionInterstitials( + session, + vmap.adBreaks, + ); + appendInterstitials(session.interstitials, interstitials); storeSession = true; } @@ -196,6 +216,8 @@ export function mapAdBreaksToSessionInterstitials( session: Session, adBreaks: VmapAdBreak[], ) { + const interstitials: Interstitial[] = []; + for (const adBreak of adBreaks) { const timeOffset = toAdBreakTimeOffset(adBreak); @@ -205,18 +227,14 @@ export function mapAdBreaksToSessionInterstitials( const dateTime = session.startTime.plus({ seconds: timeOffset }); - if (adBreak.vastUrl) { - session.interstitials.push({ - dateTime, - vastUrl: adBreak.vastUrl, - }); - } - - if (adBreak.vastData) { - session.interstitials.push({ - dateTime, - vastData: adBreak.vastData, - }); - } + interstitials.push({ + dateTime, + vast: { + url: adBreak.vastUrl, + data: adBreak.vastData, + }, + }); } + + return interstitials; } diff --git a/packages/stitcher/src/routes/session.ts b/packages/stitcher/src/routes/session.ts index 33512e31..f1934e5f 100644 --- a/packages/stitcher/src/routes/session.ts +++ b/packages/stitcher/src/routes/session.ts @@ -39,13 +39,27 @@ export const sessionRoutes = new Elysia() t.Array( t.Object({ time: t.Union([t.Number(), t.String()]), - vastUrl: t.Optional(t.String()), - uri: t.Optional(t.String()), - type: t.Optional(t.Union([t.Literal("ad"), t.Literal("bumper")])), + assets: t.Optional( + t.Array( + t.Object({ + uri: t.String(), + kind: t.Optional( + t.Union([t.Literal("ad"), t.Literal("bumper")]), + ), + }), + ), + ), + vast: t.Optional( + t.Object({ + url: t.String(), + }), + ), + assetList: t.Optional( + t.Object({ + url: t.String(), + }), + ), }), - { - description: "Manual HLS interstitial insertion.", - }, ), ), filter: t.Optional( diff --git a/packages/stitcher/src/session.ts b/packages/stitcher/src/session.ts index cb5cbf8f..f236fcad 100644 --- a/packages/stitcher/src/session.ts +++ b/packages/stitcher/src/session.ts @@ -1,9 +1,10 @@ import { randomUUID } from "crypto"; import { DateTime } from "luxon"; import { kv } from "./adapters/kv"; +import { appendInterstitials } from "./interstitials"; import { JSON } from "./lib/json"; import { resolveUri } from "./lib/url"; -import type { Interstitial, InterstitialAssetType } from "./types"; +import type { Interstitial } from "./types"; import type { VmapParams } from "./vmap"; export interface Session { @@ -17,17 +18,24 @@ export interface Session { interstitials: Interstitial[]; } -export async function createSession(params: { - uri: string; - vmap?: { +interface SessionInterstitial { + time: number | string; + assets?: { + uri: string; + kind?: "ad" | "bumper"; + }[]; + vast?: { url: string; }; - interstitials?: { - time: string | number; - vastUrl?: string; - uri?: string; - type?: InterstitialAssetType; - }[]; + assetList?: { + url: string; + }; +} + +export async function createSession(params: { + uri: string; + vmap?: VmapParams; + interstitials?: SessionInterstitial[]; expiry?: number; }) { const id = randomUUID(); @@ -44,29 +52,11 @@ export async function createSession(params: { }; if (params.interstitials) { - params.interstitials.forEach((interstitial) => { - const dateTime = - typeof interstitial.time === "string" - ? DateTime.fromISO(interstitial.time) - : startTime.plus({ seconds: interstitial.time }); - - if (interstitial.uri) { - session.interstitials.push({ - dateTime, - asset: { - url: resolveUri(interstitial.uri), - type: interstitial.type, - }, - }); - } - - if (interstitial.vastUrl) { - session.interstitials.push({ - dateTime, - vastUrl: interstitial.vastUrl, - }); - } - }); + const interstitials = mapSessionInterstitials( + startTime, + params.interstitials, + ); + appendInterstitials(session.interstitials, interstitials); } // We'll initially store the session for 10 minutes, if it's not been consumed @@ -89,3 +79,25 @@ export async function updateSession(session: Session) { const value = JSON.stringify(session); await kv.set(`session:${session.id}`, value, session.expiry); } + +function mapSessionInterstitials( + startTime: DateTime, + interstitials: SessionInterstitial[], +) { + return interstitials.map((item) => { + const { time, assets, ...rest } = item; + const dateTime = + typeof time === "string" + ? DateTime.fromISO(time) + : startTime.plus({ seconds: time }); + + return { + dateTime, + assets: assets?.map((asset) => { + const { uri, ...rest } = asset; + return { url: resolveUri(uri), ...rest }; + }), + ...rest, + }; + }); +} diff --git a/packages/stitcher/src/types.ts b/packages/stitcher/src/types.ts index a65bc242..5326d966 100644 --- a/packages/stitcher/src/types.ts +++ b/packages/stitcher/src/types.ts @@ -1,15 +1,23 @@ import type { DateTime } from "luxon"; -export type InterstitialAssetType = "ad" | "bumper"; +export interface InterstitialVast { + url?: string; + data?: string; +} export interface InterstitialAsset { url: string; - type?: InterstitialAssetType; + duration?: number; + kind?: "ad" | "bumper"; +} + +export interface InterstitialAssetList { + url: string; } export interface Interstitial { dateTime: DateTime; - vastUrl?: string; - vastData?: string; - asset?: InterstitialAsset; + assets?: InterstitialAsset[]; + vast?: InterstitialVast; + assetList?: InterstitialAssetList; } diff --git a/packages/stitcher/src/vast.ts b/packages/stitcher/src/vast.ts index 245a761d..32ef57ed 100644 --- a/packages/stitcher/src/vast.ts +++ b/packages/stitcher/src/vast.ts @@ -3,29 +3,22 @@ import * as uuid from "uuid"; import { VASTClient } from "vast-client"; import { api } from "./lib/api-client"; import { resolveUri } from "./lib/url"; +import type { InterstitialAsset, InterstitialVast } from "./types"; import type { VastAd, VastCreativeLinear, VastResponse } from "vast-client"; const NAMESPACE_UUID_AD = "5b212a7e-d6a2-43bf-bd30-13b1ca1f9b13"; -export interface AdMedia { - masterUrl: string; - duration: number; -} - -export async function getAdMediasFromVast(params: { - vastUrl?: string; - vastData?: string; -}) { +export async function getAssetsFromVast(vast: InterstitialVast) { const vastClient = new VASTClient(); let vastResponse: VastResponse | undefined; - if (params.vastUrl) { - vastResponse = await vastClient.get(params.vastUrl); + if (vast.url) { + vastResponse = await vastClient.get(vast.url); } - if (params.vastData) { + if (vast.data) { const parser = new DOMParser(); - const xml = parser.parseFromString(params.vastData, "text/xml"); + const xml = parser.parseFromString(vast.data, "text/xml"); vastResponse = await vastClient.parseVAST(xml); } @@ -33,7 +26,7 @@ export async function getAdMediasFromVast(params: { return []; } - return await getAdMediasFromVastResponse(vastResponse); + return await mapVastResponseToAssets(vastResponse); } async function scheduleForPackage(assetId: string, url: string) { @@ -91,7 +84,7 @@ async function fetchAsset(id: string) { throw new Error(`Failed to fetch asset, got status ${status}`); } -async function mapAdMedia(ad: VastAd): Promise { +async function mapAdToAsset(ad: VastAd): Promise { const creative = getCreative(ad); if (!creative) { return null; @@ -99,13 +92,13 @@ async function mapAdMedia(ad: VastAd): Promise { const id = getAdId(creative); - let masterUrl = getCreativeStreamingUrl(creative); + let url = getCreativeStreamingUrl(creative); - if (!masterUrl) { + if (!url) { const asset = await fetchAsset(id); if (asset) { - masterUrl = resolveUri(`asset://${id}`); + url = resolveUri(`asset://${id}`); } else { const fileUrl = getCreativeStaticUrl(creative); if (fileUrl) { @@ -114,28 +107,29 @@ async function mapAdMedia(ad: VastAd): Promise { } } - if (!masterUrl) { + if (!url) { return null; } return { - masterUrl, + url: url, duration: creative.duration, + kind: "ad", }; } -async function getAdMediasFromVastResponse(response: VastResponse) { - const adMedias: AdMedia[] = []; +async function mapVastResponseToAssets(response: VastResponse) { + const assets: InterstitialAsset[] = []; for (const ad of response.ads) { - const adMedia = await mapAdMedia(ad); - if (!adMedia) { + const asset = await mapAdToAsset(ad); + if (!asset) { continue; } - adMedias.push(adMedia); + assets.push(asset); } - return adMedias; + return assets; } function getCreativeStaticUrl(creative: VastCreativeLinear) { diff --git a/packages/stitcher/test/__snapshots__/interstitials.test.ts.snap b/packages/stitcher/test/__snapshots__/interstitials.test.ts.snap index 1ca6ccae..15824c82 100644 --- a/packages/stitcher/test/__snapshots__/interstitials.test.ts.snap +++ b/packages/stitcher/test/__snapshots__/interstitials.test.ts.snap @@ -1,17 +1,32 @@ // Bun Snapshot v1, https://goo.gl/fbAQLP +exports[`getAssets should get assets by interstitials 1`] = ` +[ + { + "duration": 25, + "url": "https://mock.com/ad_1/master.m3u8", + }, + { + "kind": "ad", + "url": "https://mock.com/interstitial1/master.m3u8", + }, +] +`; + exports[`getStaticDateRanges should create dateRanges for vod 1`] = ` [ { "classId": "com.apple.hls.interstitial", "clientAttributes": { "ASSET-LIST": "stitcher-endpoint/out/asset-list.json?dt=2021-05-02T10%3A12%3A05.250%2B00%3A00&sid=36bab417-0952-4c23-bdf0-9a424e4651ad", + "CONTENT-MAY-VARY": "YES", "CUE": "ONCE,PRE", "RESTRICT": "SKIP,JUMP", "RESUME-OFFSET": 0, - "SPRS-TYPES": "ad", + "TIMELINE-OCCUPIES": "POINT", + "TIMELINE-STYLE": "HIGHLIGHT", }, - "id": "1619950325", + "id": "1619950325250", "startDate": DateTime { "_zone": SystemZone {}, "c": { @@ -54,12 +69,14 @@ exports[`getStaticDateRanges should create dateRanges for vod 1`] = ` "classId": "com.apple.hls.interstitial", "clientAttributes": { "ASSET-LIST": "stitcher-endpoint/out/asset-list.json?dt=2021-05-02T10%3A12%3A15.250%2B00%3A00&sid=36bab417-0952-4c23-bdf0-9a424e4651ad", + "CONTENT-MAY-VARY": "YES", "CUE": "ONCE", "RESTRICT": "SKIP,JUMP", "RESUME-OFFSET": 0, - "SPRS-TYPES": "ad", + "TIMELINE-OCCUPIES": "POINT", + "TIMELINE-STYLE": "HIGHLIGHT", }, - "id": "1619950335", + "id": "1619950335250", "startDate": DateTime { "_zone": SystemZone {}, "c": { @@ -102,12 +119,14 @@ exports[`getStaticDateRanges should create dateRanges for vod 1`] = ` "classId": "com.apple.hls.interstitial", "clientAttributes": { "ASSET-LIST": "stitcher-endpoint/out/asset-list.json?dt=2021-05-02T10%3A12%3A35.250%2B00%3A00&sid=36bab417-0952-4c23-bdf0-9a424e4651ad", + "CONTENT-MAY-VARY": "YES", "CUE": "ONCE", "RESTRICT": "SKIP,JUMP", "RESUME-OFFSET": 0, - "SPRS-TYPES": "bumper", + "TIMELINE-OCCUPIES": "POINT", + "TIMELINE-STYLE": "PRIMARY", }, - "id": "1619950355", + "id": "1619950355250", "startDate": DateTime { "_zone": SystemZone {}, "c": { @@ -150,12 +169,14 @@ exports[`getStaticDateRanges should create dateRanges for vod 1`] = ` "classId": "com.apple.hls.interstitial", "clientAttributes": { "ASSET-LIST": "stitcher-endpoint/out/asset-list.json?dt=2021-05-02T10%3A12%3A45.250%2B00%3A00&sid=36bab417-0952-4c23-bdf0-9a424e4651ad", + "CONTENT-MAY-VARY": "YES", "CUE": "ONCE", "RESTRICT": "SKIP,JUMP", "RESUME-OFFSET": 0, - "SPRS-TYPES": "ad", + "TIMELINE-OCCUPIES": "POINT", + "TIMELINE-STYLE": "HIGHLIGHT", }, - "id": "1619950365", + "id": "1619950365250", "startDate": DateTime { "_zone": SystemZone {}, "c": { @@ -198,11 +219,64 @@ exports[`getStaticDateRanges should create dateRanges for vod 1`] = ` "classId": "com.apple.hls.interstitial", "clientAttributes": { "ASSET-LIST": "stitcher-endpoint/out/asset-list.json?dt=2021-05-02T10%3A13%3A45.250%2B00%3A00&sid=36bab417-0952-4c23-bdf0-9a424e4651ad", + "CONTENT-MAY-VARY": "YES", "CUE": "ONCE", "RESTRICT": "SKIP,JUMP", "RESUME-OFFSET": 0, + "TIMELINE-OCCUPIES": "POINT", + "TIMELINE-STYLE": "PRIMARY", }, - "id": "1619950425", + "id": "1619950425250", + "startDate": DateTime { + "_zone": SystemZone {}, + "c": { + "day": 2, + "hour": 10, + "millisecond": 250, + "minute": 13, + "month": 5, + "second": 45, + "year": 2021, + }, + "invalid": null, + "isLuxonDateTime": true, + "loc": Locale { + "eraCache": {}, + "fastNumbersCached": null, + "intl": "en-US", + "locale": "en-US", + "meridiemCache": null, + "monthsCache": { + "format": {}, + "standalone": {}, + }, + "numberingSystem": null, + "outputCalendar": null, + "specifiedLocale": null, + "weekSettings": null, + "weekdaysCache": { + "format": {}, + "standalone": {}, + }, + }, + "localWeekData": null, + "o": -0, + "ts": 1619950425250, + "weekData": null, + }, + }, + { + "classId": "com.apple.hls.interstitial", + "clientAttributes": { + "ASSET-LIST": "stitcher-endpoint/out/asset-list.json?dt=2021-05-02T10%3A13%3A45.250%2B00%3A00&sid=36bab417-0952-4c23-bdf0-9a424e4651ad", + "CONTENT-MAY-VARY": "YES", + "CUE": "ONCE", + "RESTRICT": "SKIP,JUMP", + "RESUME-OFFSET": 0, + "TIMELINE-OCCUPIES": "POINT", + "TIMELINE-STYLE": "PRIMARY", + }, + "id": "1619950425250", "startDate": DateTime { "_zone": SystemZone {}, "c": { @@ -250,11 +324,13 @@ exports[`getStaticDateRanges should create dateRanges for live 1`] = ` "classId": "com.apple.hls.interstitial", "clientAttributes": { "ASSET-LIST": "stitcher-endpoint/out/asset-list.json?dt=2021-05-02T10%3A12%3A05.250%2B00%3A00&sid=36bab417-0952-4c23-bdf0-9a424e4651ad", + "CONTENT-MAY-VARY": "YES", "CUE": "ONCE,PRE", "RESTRICT": "SKIP,JUMP", - "SPRS-TYPES": "ad", + "TIMELINE-OCCUPIES": "POINT", + "TIMELINE-STYLE": "HIGHLIGHT", }, - "id": "1619950325", + "id": "1619950325250", "startDate": DateTime { "_zone": SystemZone {}, "c": { @@ -297,11 +373,13 @@ exports[`getStaticDateRanges should create dateRanges for live 1`] = ` "classId": "com.apple.hls.interstitial", "clientAttributes": { "ASSET-LIST": "stitcher-endpoint/out/asset-list.json?dt=2021-05-02T10%3A12%3A15.250%2B00%3A00&sid=36bab417-0952-4c23-bdf0-9a424e4651ad", + "CONTENT-MAY-VARY": "YES", "CUE": "ONCE", "RESTRICT": "SKIP,JUMP", - "SPRS-TYPES": "ad", + "TIMELINE-OCCUPIES": "POINT", + "TIMELINE-STYLE": "HIGHLIGHT", }, - "id": "1619950335", + "id": "1619950335250", "startDate": DateTime { "_zone": SystemZone {}, "c": { @@ -344,11 +422,13 @@ exports[`getStaticDateRanges should create dateRanges for live 1`] = ` "classId": "com.apple.hls.interstitial", "clientAttributes": { "ASSET-LIST": "stitcher-endpoint/out/asset-list.json?dt=2021-05-02T10%3A12%3A35.250%2B00%3A00&sid=36bab417-0952-4c23-bdf0-9a424e4651ad", + "CONTENT-MAY-VARY": "YES", "CUE": "ONCE", "RESTRICT": "SKIP,JUMP", - "SPRS-TYPES": "bumper", + "TIMELINE-OCCUPIES": "POINT", + "TIMELINE-STYLE": "PRIMARY", }, - "id": "1619950355", + "id": "1619950355250", "startDate": DateTime { "_zone": SystemZone {}, "c": { @@ -391,11 +471,13 @@ exports[`getStaticDateRanges should create dateRanges for live 1`] = ` "classId": "com.apple.hls.interstitial", "clientAttributes": { "ASSET-LIST": "stitcher-endpoint/out/asset-list.json?dt=2021-05-02T10%3A12%3A45.250%2B00%3A00&sid=36bab417-0952-4c23-bdf0-9a424e4651ad", + "CONTENT-MAY-VARY": "YES", "CUE": "ONCE", "RESTRICT": "SKIP,JUMP", - "SPRS-TYPES": "ad", + "TIMELINE-OCCUPIES": "POINT", + "TIMELINE-STYLE": "HIGHLIGHT", }, - "id": "1619950365", + "id": "1619950365250", "startDate": DateTime { "_zone": SystemZone {}, "c": { @@ -438,10 +520,62 @@ exports[`getStaticDateRanges should create dateRanges for live 1`] = ` "classId": "com.apple.hls.interstitial", "clientAttributes": { "ASSET-LIST": "stitcher-endpoint/out/asset-list.json?dt=2021-05-02T10%3A13%3A45.250%2B00%3A00&sid=36bab417-0952-4c23-bdf0-9a424e4651ad", + "CONTENT-MAY-VARY": "YES", + "CUE": "ONCE", + "RESTRICT": "SKIP,JUMP", + "TIMELINE-OCCUPIES": "POINT", + "TIMELINE-STYLE": "PRIMARY", + }, + "id": "1619950425250", + "startDate": DateTime { + "_zone": SystemZone {}, + "c": { + "day": 2, + "hour": 10, + "millisecond": 250, + "minute": 13, + "month": 5, + "second": 45, + "year": 2021, + }, + "invalid": null, + "isLuxonDateTime": true, + "loc": Locale { + "eraCache": {}, + "fastNumbersCached": null, + "intl": "en-US", + "locale": "en-US", + "meridiemCache": null, + "monthsCache": { + "format": {}, + "standalone": {}, + }, + "numberingSystem": null, + "outputCalendar": null, + "specifiedLocale": null, + "weekSettings": null, + "weekdaysCache": { + "format": {}, + "standalone": {}, + }, + }, + "localWeekData": null, + "o": -0, + "ts": 1619950425250, + "weekData": null, + }, + }, + { + "classId": "com.apple.hls.interstitial", + "clientAttributes": { + "ASSET-LIST": "stitcher-endpoint/out/asset-list.json?dt=2021-05-02T10%3A13%3A45.250%2B00%3A00&sid=36bab417-0952-4c23-bdf0-9a424e4651ad", + "CONTENT-MAY-VARY": "YES", "CUE": "ONCE", "RESTRICT": "SKIP,JUMP", + "TIMELINE-OCCUPIES": "POINT", + "TIMELINE-STYLE": "PRIMARY", }, - "id": "1619950425", + "id": "1619950425250", "startDate": DateTime { "_zone": SystemZone {}, "c": { diff --git a/packages/stitcher/test/__snapshots__/playlist.test.ts.snap b/packages/stitcher/test/__snapshots__/playlist.test.ts.snap index abe06f17..8345dd43 100644 --- a/packages/stitcher/test/__snapshots__/playlist.test.ts.snap +++ b/packages/stitcher/test/__snapshots__/playlist.test.ts.snap @@ -132,7 +132,10 @@ exports[`mapAdBreaksToSessionInterstitials should handle time based with vastUrl "ts": 1619950325250, "weekData": null, }, - "vastUrl": "http://mock.com/vast_1.xml", + "vast": { + "data": undefined, + "url": "http://mock.com/vast_1.xml", + }, }, { "dateTime": DateTime { @@ -172,7 +175,10 @@ exports[`mapAdBreaksToSessionInterstitials should handle time based with vastUrl "ts": 1619950340250, "weekData": null, }, - "vastUrl": "http://mock.com/vast_2.xml", + "vast": { + "data": undefined, + "url": "http://mock.com/vast_2.xml", + }, }, { "dateTime": DateTime { @@ -212,7 +218,10 @@ exports[`mapAdBreaksToSessionInterstitials should handle time based with vastUrl "ts": 1619950350250, "weekData": null, }, - "vastUrl": "http://mock.com/vast_3.xml", + "vast": { + "data": undefined, + "url": "http://mock.com/vast_3.xml", + }, }, ] `; @@ -257,7 +266,10 @@ exports[`mapAdBreaksToSessionInterstitials should handle vastData 1`] = ` "ts": 1619950335250, "weekData": null, }, - "vastData": "mocked VAST data", + "vast": { + "data": "mocked VAST data", + "url": undefined, + }, }, ] `; diff --git a/packages/stitcher/test/interstitials.test.ts b/packages/stitcher/test/interstitials.test.ts index b502f957..3fbd8b79 100644 --- a/packages/stitcher/test/interstitials.test.ts +++ b/packages/stitcher/test/interstitials.test.ts @@ -1,10 +1,12 @@ -import { describe, expect, test } from "bun:test"; -import { mockSessionWithInterstitials } from "./mock"; -import { getStaticDateRanges } from "../src/interstitials"; +import { describe, expect, spyOn, test } from "bun:test"; +import { DateTime } from "luxon"; +import { addFakeInterstitials, fakeSession } from "./test-data"; +import { getAssets, getStaticDateRanges } from "../src/interstitials"; describe("getStaticDateRanges", () => { test("should create dateRanges for vod", () => { - const session = mockSessionWithInterstitials(); + const session = fakeSession(); + addFakeInterstitials(session); const isLive = false; const dateRanges = getStaticDateRanges(session, isLive); @@ -13,7 +15,8 @@ describe("getStaticDateRanges", () => { }); test("should create dateRanges for live", () => { - const session = mockSessionWithInterstitials(); + const session = fakeSession(); + addFakeInterstitials(session); const isLive = true; const dateRanges = getStaticDateRanges(session, isLive); @@ -21,3 +24,42 @@ describe("getStaticDateRanges", () => { expect(dateRanges).toMatchSnapshot(); }); }); + +describe("getAssets", () => { + test("should get assets by interstitials", async () => { + const session = fakeSession(); + const dateTime = DateTime.now(); + + const spy = spyOn(await import("../src/vast"), "getAssetsFromVast"); + spy.mockReturnValueOnce( + Promise.resolve([ + { + url: "https://mock.com/ad_1/master.m3u8", + duration: 25, + }, + ]), + ); + + session.interstitials = [ + { + dateTime, + assets: [ + { + url: "https://mock.com/interstitial1/master.m3u8", + kind: "ad", + }, + ], + vast: { + url: "https://mock.com/vast.xml", + }, + }, + ]; + + const assets = await getAssets(session, dateTime); + + expect(spy).toHaveBeenCalledTimes(1); + expect(assets).toMatchSnapshot(); + + spy.mockRestore(); + }); +}); diff --git a/packages/stitcher/test/playlist.test.ts b/packages/stitcher/test/playlist.test.ts index f1ad5690..0b4445ac 100644 --- a/packages/stitcher/test/playlist.test.ts +++ b/packages/stitcher/test/playlist.test.ts @@ -1,10 +1,10 @@ import { describe, expect, test } from "bun:test"; import { - mockMaster, - mockMediaWithAbsSeg, - mockMediaWithRelSeg, - mockSession, -} from "./mock"; + fakeMasterPlaylist, + fakeMediaPlaylistWithAbsSeg, + fakeMediaPlaylistWithRelSeg, + fakeSession, +} from "./test-data"; import { createMasterUrl, mapAdBreaksToSessionInterstitials, @@ -14,7 +14,7 @@ import { describe("rewriteMasterPlaylistUrls", () => { test("should rewrite", () => { - const master = mockMaster(); + const master = fakeMasterPlaylist(); rewriteMasterPlaylistUrls(master, { origUrl: "http://mock.com/master.m3u8", @@ -24,8 +24,8 @@ describe("rewriteMasterPlaylistUrls", () => { }); test("should include session id", () => { - const master = mockMaster(); - const session = mockSession(); + const master = fakeMasterPlaylist(); + const session = fakeSession(); rewriteMasterPlaylistUrls(master, { origUrl: "http://mock.com/master.m3u8", @@ -38,7 +38,7 @@ describe("rewriteMasterPlaylistUrls", () => { describe("rewriteMediaPlaylistUrls", () => { test("should rewrite relative segments", () => { - const media = mockMediaWithRelSeg(); + const media = fakeMediaPlaylistWithRelSeg(); rewriteMediaPlaylistUrls(media, "https://mock.com/video_1.m3u8"); @@ -46,7 +46,7 @@ describe("rewriteMediaPlaylistUrls", () => { }); test("should rewrite absolute segments", () => { - const media = mockMediaWithAbsSeg(); + const media = fakeMediaPlaylistWithAbsSeg(); rewriteMediaPlaylistUrls(media, "https://mock.com/video_2.m3u8"); @@ -56,9 +56,9 @@ describe("rewriteMediaPlaylistUrls", () => { describe("mapAdBreaksToSessionInterstitials", () => { test("should handle time based with vastUrl", () => { - const session = mockSession(); + const session = fakeSession(); - mapAdBreaksToSessionInterstitials(session, [ + const interstitials = mapAdBreaksToSessionInterstitials(session, [ { timeOffset: "start", vastUrl: "http://mock.com/vast_1.xml", @@ -73,20 +73,20 @@ describe("mapAdBreaksToSessionInterstitials", () => { }, ]); - expect(session.interstitials).toMatchSnapshot(); + expect(interstitials).toMatchSnapshot(); }); test("should handle vastData", () => { - const session = mockSession(); + const session = fakeSession(); - mapAdBreaksToSessionInterstitials(session, [ + const interstitials = mapAdBreaksToSessionInterstitials(session, [ { timeOffset: "00:00:10.000", vastData: "mocked VAST data", }, ]); - expect(session.interstitials).toMatchSnapshot(); + expect(interstitials).toMatchSnapshot(); }); }); @@ -105,7 +105,7 @@ describe("createMasterUrl", () => { }); test("should create with session", () => { - const session = mockSession(); + const session = fakeSession(); const result = createMasterUrl({ url: "https://mock.com/master.m3u8", diff --git a/packages/stitcher/test/setup.ts b/packages/stitcher/test/setup.ts index 7d8da196..779b326b 100644 --- a/packages/stitcher/test/setup.ts +++ b/packages/stitcher/test/setup.ts @@ -15,6 +15,12 @@ mock.module("redis", () => ({ }, })); +mock.module("vast-client", () => ({ + VASTClient: class { + get = mock(); + }, +})); + // The day my son was born! setSystemTime(new Date(2021, 4, 2, 10, 12, 5, 250)); diff --git a/packages/stitcher/test/mock.ts b/packages/stitcher/test/test-data.ts similarity index 57% rename from packages/stitcher/test/mock.ts rename to packages/stitcher/test/test-data.ts index fc8c0de8..d3bca6a7 100644 --- a/packages/stitcher/test/mock.ts +++ b/packages/stitcher/test/test-data.ts @@ -2,7 +2,7 @@ import { DateTime } from "luxon"; import type { MasterPlaylist, MediaPlaylist } from "../src/parser"; import type { Session } from "../src/session"; -export function mockMaster(): MasterPlaylist { +export function fakeMasterPlaylist(): MasterPlaylist { return { variants: [ { @@ -29,7 +29,7 @@ export function mockMaster(): MasterPlaylist { }; } -export function mockMediaWithRelSeg(): MediaPlaylist { +export function fakeMediaPlaylistWithRelSeg(): MediaPlaylist { return { targetDuration: 2, endlist: true, @@ -50,7 +50,7 @@ export function mockMediaWithRelSeg(): MediaPlaylist { }; } -export function mockMediaWithAbsSeg(): MediaPlaylist { +export function fakeMediaPlaylistWithAbsSeg(): MediaPlaylist { return { targetDuration: 2, endlist: true, @@ -67,7 +67,7 @@ export function mockMediaWithAbsSeg(): MediaPlaylist { }; } -export function mockSession(): Session { +export function fakeSession(): Session { return { id: "36bab417-0952-4c23-bdf0-9a424e4651ad", url: "http://mock.com/master.m3u8", @@ -77,50 +77,56 @@ export function mockSession(): Session { }; } -export function mockSessionWithInterstitials(): Session { - const session = mockSession(); - - const startDate = DateTime.now(); - +export function addFakeInterstitials(session: Session) { session.interstitials = [ { - dateTime: startDate, - vastUrl: "https://mock.com/vast.xml", + dateTime: session.startTime, + vast: { + url: "https://mock.com/vast.xml", + }, }, { - dateTime: startDate.plus({ seconds: 10 }), - vastUrl: "mocked VAST data", + dateTime: session.startTime.plus({ seconds: 10 }), + vast: { + data: "mocked VAST data", + }, }, // Manual bumper interstitial { - dateTime: startDate.plus({ seconds: 30 }), - asset: { - url: "https://mock.com/interstitial/bumper.m3u8", - type: "bumper", - }, + dateTime: session.startTime.plus({ seconds: 30 }), + assets: [ + { + url: "https://mock.com/interstitial/bumper.m3u8", + kind: "bumper", + }, + ], }, // Manual ad interstitial { - dateTime: startDate.plus({ seconds: 40 }), - asset: { - url: "https://mock.com/interstitial/ad.m3u8", - type: "ad", - }, + dateTime: session.startTime.plus({ seconds: 40 }), + assets: [ + { + url: "https://mock.com/interstitial/ad.m3u8", + kind: "ad", + }, + ], }, // Multiple manual interstitials { - dateTime: startDate.plus({ seconds: 100 }), - asset: { - url: "https://mock.com/interstitial/master1.m3u8", - }, + dateTime: session.startTime.plus({ seconds: 100 }), + assets: [ + { + url: "https://mock.com/interstitial/master1.m3u8", + }, + ], }, { - dateTime: startDate.plus({ seconds: 100 }), - asset: { - url: "https://mock.com/interstitial/master2.m3u8", - }, + dateTime: session.startTime.plus({ seconds: 100 }), + assets: [ + { + url: "https://mock.com/interstitial/master2.m3u8", + }, + ], }, ]; - - return session; }