From 81268299d0a6c9bcd6446873e07b74167788ef48 Mon Sep 17 00:00:00 2001 From: Matthias Van Parijs Date: Thu, 29 Aug 2024 22:28:26 +0200 Subject: [PATCH] feat: maxResolution for master manifest --- packages/stitcher/package.json | 4 +-- packages/stitcher/src/contract.ts | 1 + packages/stitcher/src/playlist.ts | 7 ++++++ packages/stitcher/src/session.ts | 8 ++++++ packages/stitcher/src/types.ts | 1 + packages/stitcher/test/playlist.test.ts | 33 +++++++++++++++++++++++++ 6 files changed, 52 insertions(+), 2 deletions(-) diff --git a/packages/stitcher/package.json b/packages/stitcher/package.json index 20a0b9f5..620a690f 100644 --- a/packages/stitcher/package.json +++ b/packages/stitcher/package.json @@ -3,8 +3,8 @@ "type": "module", "scripts": { "build": "tsc", - "start": "node ./dist/index.js", - "dev": "tsc-watch --noClear --onSuccess \"node ./dist/index.js\"", + "start": "node ./dist/src/index.js", + "dev": "tsc-watch --noClear --onSuccess \"node ./dist/src/index.js\"", "test": "NODE_OPTIONS=--experimental-vm-modules jest" }, "devDependencies": { diff --git a/packages/stitcher/src/contract.ts b/packages/stitcher/src/contract.ts index f36d66c5..6c367cb8 100644 --- a/packages/stitcher/src/contract.ts +++ b/packages/stitcher/src/contract.ts @@ -15,6 +15,7 @@ export const postSessionBodySchema = z.object({ ) .optional(), bumperAssetId: z.string().optional(), + maxResolution: z.number().optional(), }); export const contract = c.router({ diff --git a/packages/stitcher/src/playlist.ts b/packages/stitcher/src/playlist.ts index 29226757..f0597b87 100644 --- a/packages/stitcher/src/playlist.ts +++ b/packages/stitcher/src/playlist.ts @@ -16,6 +16,13 @@ export async function formatMasterPlaylist(session: Session) { const master = await fetchPlaylist(url); + master.variants = master.variants.filter((variant) => { + if (!variant.resolution) { + return true; + } + return variant.resolution.height <= session.maxResolution; + }); + return stringify(master); } diff --git a/packages/stitcher/src/session.ts b/packages/stitcher/src/session.ts index e4fb1c42..03140a2f 100644 --- a/packages/stitcher/src/session.ts +++ b/packages/stitcher/src/session.ts @@ -15,6 +15,7 @@ export async function createSession(data: { vmapUrl?: string; interstitials?: Interstitial[]; bumperAssetId?: string; + maxResolution?: number; }) { const sessionId = randomUUID(); @@ -42,10 +43,17 @@ export async function createSession(data: { }); } + let maxResolution = data.maxResolution; + if (!maxResolution) { + const MAX_RESOLUTION_8K = 4320; + maxResolution = MAX_RESOLUTION_8K; + } + const session = { id: sessionId, assetId: data.assetId, interstitials, + maxResolution, } satisfies Session; const redisKey = getRedisKey(sessionId); diff --git a/packages/stitcher/src/types.ts b/packages/stitcher/src/types.ts index 989cdca9..f3dc7c5f 100644 --- a/packages/stitcher/src/types.ts +++ b/packages/stitcher/src/types.ts @@ -2,6 +2,7 @@ export type Session = { id: string; assetId: string; interstitials: Interstitial[]; + maxResolution: number; }; export type Interstitial = { diff --git a/packages/stitcher/test/playlist.test.ts b/packages/stitcher/test/playlist.test.ts index 9af48694..a9b2faee 100644 --- a/packages/stitcher/test/playlist.test.ts +++ b/packages/stitcher/test/playlist.test.ts @@ -22,6 +22,7 @@ describe("playlist", () => { id: "sessionId", assetId: "assetId", interstitials: [], + maxResolution: 1080, }); expect(master).toMatchInlineSnapshot(` @@ -64,6 +65,7 @@ describe("playlist", () => { id: "sessionId", assetId: "assetId", interstitials: [], + maxResolution: 1080, }, "video_1080" ); @@ -135,6 +137,7 @@ describe("playlist", () => { assetId: "ad3", }, ], + maxResolution: 1080, }, "video_1080" ); @@ -161,4 +164,34 @@ describe("playlist", () => { #EXT-X-DATERANGE:ID="10",CLASS="com.apple.hls.interstitial",START-DATE="2022-02-21T17:35:10.000Z",X-ASSET-LIST="/session/sessionId/asset-list.json?timeOffset=10",X-RESUME-OFFSET=0,X-RESTRICT="SKIP,JUMP"" `); }); + + test("should remove resolutions below maxResolution", async () => { + fetchMock.mock("https://s3-public.com/package/assetId/hls/master.m3u8", { + status: 200, + body: ` + #EXTM3U + #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=2149280,CODECS="mp4a.40.2,avc1.64001f",RESOLUTION=100x720,NAME="720" + 720.m3u8 + #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=2149280,CODECS="mp4a.40.2,avc1.64001f",RESOLUTION=100x480,NAME="480" + 480.m3u8 + #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=2149280,CODECS="mp4a.40.2,avc1.64001f",RESOLUTION=100x360,NAME="360" + 360.m3u8 + `, + }); + + const master = await formatMasterPlaylist({ + id: "sessionId", + assetId: "assetId", + interstitials: [], + maxResolution: 480, + }); + + expect(master).toMatchInlineSnapshot(` + "#EXTM3U + #EXT-X-STREAM-INF:BANDWIDTH=2149280,CODECS="mp4a.40.2,avc1.64001f",RESOLUTION=100x480,PROGRAM-ID=1 + 480.m3u8 + #EXT-X-STREAM-INF:BANDWIDTH=2149280,CODECS="mp4a.40.2,avc1.64001f",RESOLUTION=100x360,PROGRAM-ID=1 + 360.m3u8" + `); + }); });