initial commit
This commit is contained in:
+153
@@ -0,0 +1,153 @@
|
||||
import Elysia, { t } from "elysia";
|
||||
import { Document } from "@/srv/types/DocumentSchema";
|
||||
import { CaptionStyleSchema } from "@/srv/types/CaptionStyleSchema";
|
||||
import { S3Service } from "@/srv/services/s3";
|
||||
import { renderCaptionedVideo } from "@/srv/services/render_video";
|
||||
import {
|
||||
cancelRender,
|
||||
enqueueRender,
|
||||
renderQueue,
|
||||
startRenderWorker,
|
||||
} from "@/srv/services/render_queue";
|
||||
import { serverConfig } from "@/srv/config";
|
||||
|
||||
// Start the BullMQ worker
|
||||
const worker = startRenderWorker();
|
||||
console.log(
|
||||
`Render worker started (concurrency: ${serverConfig.MAX_CONCURRENT_RENDERS})`,
|
||||
);
|
||||
|
||||
const app = new Elysia({ prefix: "/api" });
|
||||
|
||||
app.post(
|
||||
"/render",
|
||||
async ({ body, set }) => {
|
||||
const renderId = body.renderId || crypto.randomUUID();
|
||||
|
||||
// If callbackUrl is provided, enqueue async render
|
||||
if (body.callbackUrl) {
|
||||
await enqueueRender({
|
||||
renderId,
|
||||
folder: body.folder,
|
||||
videoSrc: body.videoSrc,
|
||||
transcription: body.transcription,
|
||||
styleConfig: body.styleConfig,
|
||||
callbackUrl: body.callbackUrl,
|
||||
});
|
||||
|
||||
set.status = 202;
|
||||
return { renderId, status: "queued" };
|
||||
}
|
||||
|
||||
// Sync fallback (no callbackUrl) — render immediately and return result
|
||||
const s3Service = new S3Service("captioned", body.folder);
|
||||
const filename = s3Service.getFileName(body.videoSrc);
|
||||
const videoUrl = await s3Service.getFileURL(body.videoSrc);
|
||||
|
||||
const res = await renderCaptionedVideo(
|
||||
body.transcription,
|
||||
videoUrl,
|
||||
filename,
|
||||
body.styleConfig,
|
||||
);
|
||||
|
||||
try {
|
||||
const s3OutPath = await s3Service.uploadFile(res.output, filename);
|
||||
return { output: s3OutPath };
|
||||
} finally {
|
||||
await Bun.file(res.output)
|
||||
.delete()
|
||||
.catch(() => {});
|
||||
}
|
||||
},
|
||||
{
|
||||
body: t.Object({
|
||||
folder: t.Optional(t.String()),
|
||||
renderId: t.Optional(t.String()),
|
||||
videoSrc: t.String(),
|
||||
transcription: Document,
|
||||
styleConfig: t.Optional(CaptionStyleSchema),
|
||||
callbackUrl: t.Optional(t.String()),
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
app.get(
|
||||
"/render/:renderId",
|
||||
async ({ params }) => {
|
||||
const job = await renderQueue.getJob(params.renderId);
|
||||
if (!job) {
|
||||
return { status: "not_found", renderId: params.renderId };
|
||||
}
|
||||
|
||||
const state = await job.getState();
|
||||
const progress = typeof job.progress === "number" ? job.progress : 0;
|
||||
|
||||
if (state === "completed") {
|
||||
return {
|
||||
status: "done",
|
||||
renderId: params.renderId,
|
||||
progress_pct: 100,
|
||||
output_path: job.returnvalue?.outputPath,
|
||||
callback_delivered: job.returnvalue?.callbackDelivered ?? false,
|
||||
};
|
||||
}
|
||||
|
||||
if (state === "failed") {
|
||||
return {
|
||||
status: "failed",
|
||||
renderId: params.renderId,
|
||||
error: job.failedReason,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
status: state, // "waiting", "active", "delayed"
|
||||
renderId: params.renderId,
|
||||
progress_pct: progress,
|
||||
};
|
||||
},
|
||||
{
|
||||
params: t.Object({
|
||||
renderId: t.String(),
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
app.delete(
|
||||
"/render/:renderId",
|
||||
async ({ params, set }) => {
|
||||
const result = await cancelRender(params.renderId);
|
||||
|
||||
if (result.status === "not_found") {
|
||||
set.status = 404;
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
{
|
||||
params: t.Object({
|
||||
renderId: t.String(),
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
app.get("/render", async () => "Hello");
|
||||
|
||||
app.get("/health", async () => {
|
||||
return { status: "ok" };
|
||||
});
|
||||
|
||||
app.listen(serverConfig.PORT, () => {
|
||||
console.log(
|
||||
`Remotion service listening on ${serverConfig.HOST}:${serverConfig.PORT}`,
|
||||
);
|
||||
});
|
||||
|
||||
// Graceful shutdown
|
||||
process.on("SIGTERM", async () => {
|
||||
console.log("Shutting down render worker...");
|
||||
await worker.close();
|
||||
process.exit(0);
|
||||
});
|
||||
Reference in New Issue
Block a user