Lior-0618's picture
chore: merge master → dev/video-fer (SSE transcribe-stream)
aa15e90
/**
* SSE proxy: forwards audio to Modal /transcribe/stream and pipes tokens back.
* Browser cannot call Modal directly (no CORS), so this Next.js route acts as relay.
*/
const MODAL_API_URL =
process.env.MODAL_API_URL ??
"https://yongkang-zou1999--evoxtral-api-evoxtralmodel-web.modal.run";
export async function POST(req: Request) {
const formData = await req.formData();
const audioFile = formData.get("audio") as Blob | null;
if (!audioFile) {
return new Response(JSON.stringify({ error: "Audio file is required" }), {
status: 400,
headers: { "Content-Type": "application/json" },
});
}
const MAX_UPLOAD_BYTES = 100 * 1024 * 1024;
if (audioFile.size > MAX_UPLOAD_BYTES) {
return new Response(
JSON.stringify({ error: `File exceeds ${MAX_UPLOAD_BYTES / 1024 / 1024}MB limit` }),
{ status: 413, headers: { "Content-Type": "application/json" } }
);
}
// Forward to Modal streaming endpoint, preserving original filename for format detection
const upstream = new FormData();
const originalName = (audioFile as File).name || "audio.wav";
upstream.append("file", audioFile, originalName);
const language = formData.get("language") as string | null;
if (language) upstream.append("language", language);
try {
const res = await fetch(`${MODAL_API_URL}/transcribe/stream`, {
method: "POST",
body: upstream,
signal: AbortSignal.timeout(5 * 60 * 1000),
});
if (!res.ok) {
const errText = await res.text().catch(() => "Upstream error");
return new Response(
JSON.stringify({ error: errText }),
{ status: res.status, headers: { "Content-Type": "application/json" } }
);
}
// Pipe the SSE stream through to the client
return new Response(res.body, {
headers: {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
Connection: "keep-alive",
"X-Accel-Buffering": "no",
},
});
} catch (error: unknown) {
const isTimeout =
error instanceof Error &&
(error.name === "TimeoutError" || error.name === "AbortError");
return new Response(
JSON.stringify({
error: isTimeout ? "Transcription timed out" : "Failed to reach Modal API",
}),
{ status: isTimeout ? 504 : 502, headers: { "Content-Type": "application/json" } }
);
}
}