Skip to content

Commit

Permalink
feat: Direct master.m3u8 with query params
Browse files Browse the repository at this point in the history
  • Loading branch information
matvp91 committed Aug 30, 2024
1 parent 7526da6 commit b55664d
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 17 deletions.
5 changes: 3 additions & 2 deletions packages/stitcher/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@
"esm": "^3.2.25",
"fetch-mock": "^11.1.1",
"jest": "^29.7.0",
"jest-prettier": "npm:prettier@^2",
"ts-jest": "^29.2.5",
"tsc-watch": "^6.2.0",
"typescript": "^5.5.4",
"jest-prettier": "npm:prettier@^2"
"typescript": "^5.5.4"
},
"dependencies": {
"@fastify/cors": "^9.0.1",
Expand All @@ -35,6 +35,7 @@
"fastify": "^4.28.1",
"find-config": "^1.0.0",
"hh-mm-ss": "^1.2.0",
"hi-base64": "^0.3.1",
"parse-filepath": "^1.0.2",
"redis": "^4.7.0",
"uuid": "^10.0.0",
Expand Down
50 changes: 47 additions & 3 deletions packages/stitcher/src/contract.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { initContract } from "@ts-rest/core";
import * as z from "zod";
import base64 from "hi-base64";

const c = initContract();

export const postSessionBodySchema = z.object({
assetId: z.string(),
const sessionParams = z.object({
vmapUrl: z.string().optional(),
interstitials: z
.array(
Expand All @@ -15,16 +15,34 @@ export const postSessionBodySchema = z.object({
)
.optional(),
bumperAssetId: z.string().optional(),
maxResolution: z.number().optional(),
maxResolution: z.coerce.number().optional(),
});

export const postSessionBodySchema = z
.object({
assetId: z.string(),
})
.merge(sessionParams);

export const getDirectMasterPlaylistQuerySchema = z
.object({
params: base64Type(sessionParams).optional(),
})
.merge(sessionParams);

export const contract = c.router({
postSession: {
method: "POST",
path: "/session",
body: postSessionBodySchema,
responses: {},
},
getDirectMasterPlaylist: {
method: "GET",
path: "/direct/:assetId/master.m3u8",
responses: {},
query: getDirectMasterPlaylistQuerySchema,
},
getMasterPlaylist: {
method: "GET",
path: "/session/:sessionId/master.m3u8",
Expand All @@ -49,3 +67,29 @@ export const contract = c.router({
responses: {},
},
});

function base64Type<T extends z.AnyZodObject>(schema: T) {
return z.string().transform((value) => {
const raw = base64.decode(value);
const obj = JSON.parse(raw);
return schema.parse(obj);
});
}

function recursiveToCamel(item: unknown) {
if (Array.isArray(item)) {
return item.map((el: unknown) => recursiveToCamel(el));
} else if (typeof item === "function" || item !== Object(item)) {
return item;
}
return Object.fromEntries(
Object.entries(item as Record<string, unknown>).map(
([key, value]: [string, unknown]) => [
key.replace(/([-_][a-z])/gi, (c) =>
c.toUpperCase().replace(/[-_]/g, ""),
),
recursiveToCamel(value),
],
),
);
}
30 changes: 18 additions & 12 deletions packages/stitcher/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,27 +31,33 @@ async function buildServer() {
},
};
},
getDirectMasterPlaylist: async ({ request, params, query, reply }) => {
const body = {
assetId: params.assetId,
// Use params from base64 payload first.
...query.params,
// Overwrite them with query params.
...query,
};

const session = await createSession(body);

return reply.redirect(
`${request.protocol}://${request.hostname}/session/${session.id}/master.m3u8`,
302,
);
},
getMasterPlaylist: async ({ params, reply }) => {
const session = await getSession(params.sessionId);
const response = await formatMasterPlaylist(session);

reply.type("application/x-mpegURL");

return {
status: 200,
body: response,
};
return reply.type("application/x-mpegURL").send(response);
},
getMediaPlaylist: async ({ params, reply }) => {
const session = await getSession(params.sessionId);
const response = await formatMediaPlaylist(session, params.path);

reply.type("application/x-mpegURL");

return {
status: 200,
body: response,
};
return reply.type("application/x-mpegURL").send(response);
},
getAssetList: async ({ query, params }) => {
const session = await getSession(params.sessionId);
Expand Down
7 changes: 7 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit b55664d

Please sign in to comment.