From ee9125b427456ca9a85127597e577e27aede0329 Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Tue, 14 Apr 2026 23:06:49 +0200 Subject: [PATCH] Update prepare caller --- api/src/controllers/video-metadata.ts | 74 ++++++++++++++++++++++++++- api/src/websockets.ts | 23 ++------- 2 files changed, 75 insertions(+), 22 deletions(-) diff --git a/api/src/controllers/video-metadata.ts b/api/src/controllers/video-metadata.ts index c4837b52..53ebb5ae 100644 --- a/api/src/controllers/video-metadata.ts +++ b/api/src/controllers/video-metadata.ts @@ -1,13 +1,16 @@ -import { eq } from "drizzle-orm"; +import { getLogger } from "@logtape/logtape"; +import { eq, and } from "drizzle-orm"; import { Elysia, t } from "elysia"; import slugify from "slugify"; import { auth } from "~/auth"; import { db } from "~/db"; -import { entryVideoJoin, videos } from "~/db/schema"; +import { entries, entryVideoJoin, videos } from "~/db/schema"; import { KError } from "~/models/error"; import { isUuid } from "~/models/utils"; import { Video } from "~/models/video"; +const logger = getLogger(); + export const videosMetadata = new Elysia({ prefix: "/videos", tags: ["videos"], @@ -188,4 +191,71 @@ export const videosMetadata = new Elysia({ }, }, }, + ) + .get( + ":id/prepare", + async ({ params: { id }, headers: { authorization } }) => { + await prepareVideo(id, authorization!); + }, + { + detail: { description: "Prepare a video for playback" }, + params: t.Object({ + id: t.String({ + description: "The id or slug of the video to watch.", + example: "made-in-abyss-s1e13", + }), + }), + response: { + 302: t.Void({ + description: + "Prepare said video for playback (compute everything possible and cache it)", + }), + 404: { + ...KError, + description: "No video found with the given id or slug.", + }, + }, + }, ); + +export const prepareVideo = async (slug: string, auth: string) => { + logger.info("Preparing next video {slug}", { slug }); + const [vid] = await db + .select({ path: videos.path, show: entries.showPk, order: entries.order }) + .from(videos) + .innerJoin(entryVideoJoin, eq(videos.pk, entryVideoJoin.videoPk)) + .leftJoin(entries, eq(entries.pk, entryVideoJoin.entryPk)) + .where(eq(entryVideoJoin.slug, slug)) + .limit(1); + + const related = vid.show + ? await db + .select({ order: entries.order, path: videos.path }) + .from(entries) + .innerJoin(entryVideoJoin, eq(entries.pk, entryVideoJoin.entryPk)) + .innerJoin(videos, eq(videos.pk, entryVideoJoin.videoPk)) + .where(and(eq(entries.showPk, vid.show), eq(entries.kind, "episode"))) + .orderBy(entries.order) + : []; + const idx = related.findIndex((x) => x.order === vid.order); + + const path = Buffer.from(vid.path, "utf8").toString("base64url"); + await fetch( + new URL( + `/video/${path}/prepare`, + process.env.TRANSCODER_SERVER ?? "http://transcoder:7666", + ), + { + headers: { + authorization: auth, + "content-type": "application/json", + }, + method: "POST", + body: JSON.stringify({ + nearEpisodes: [related[idx - 1], related[idx + 1]] + .filter((x) => x) + .map((x) => x.path), + }), + }, + ); +}; diff --git a/api/src/websockets.ts b/api/src/websockets.ts index d41792dc..e885cc70 100644 --- a/api/src/websockets.ts +++ b/api/src/websockets.ts @@ -1,15 +1,13 @@ -import { getLogger } from "@logtape/logtape"; import type { TObject, TString } from "@sinclair/typebox"; import { eq } from "drizzle-orm"; import Elysia, { type TSchema, t } from "elysia"; import { auth } from "./auth"; import { updateProgress } from "./controllers/profiles/history"; import { getOrCreateProfile } from "./controllers/profiles/profile"; +import { prepareVideo } from "./controllers/video-metadata"; import { getVideos } from "./controllers/videos"; import { videos } from "./db/schema"; -const logger = getLogger(); - const actionMap = { ping: handler({ message(ws) { @@ -61,23 +59,8 @@ const actionMap = { languages: ["*"], userId: ws.data.jwt.sub, }); - if (!vid) return; - - logger.info("Preparing next video {videoId}", { - videoId: vid.id, - }); - const path = Buffer.from(vid.path, "utf8").toString("base64url"); - await fetch( - new URL( - `/video/${path}/prepare`, - process.env.TRANSCODER_SERVER ?? "http://transcoder:7666", - ), - { - headers: { - authorization: ws.data.headers.authorization!, - }, - }, - ); + const next = vid?.next?.video; + if (next) await prepareVideo(next, ws.data.headers.authorization!); } }, }),