summaryrefslogtreecommitdiff
path: root/apps/web/src/app/api/transcribe
diff options
context:
space:
mode:
Diffstat (limited to 'apps/web/src/app/api/transcribe')
-rw-r--r--apps/web/src/app/api/transcribe/route.ts189
1 files changed, 0 insertions, 189 deletions
diff --git a/apps/web/src/app/api/transcribe/route.ts b/apps/web/src/app/api/transcribe/route.ts
deleted file mode 100644
index 9a497f65..00000000
--- a/apps/web/src/app/api/transcribe/route.ts
+++ /dev/null
@@ -1,189 +0,0 @@
-import { NextRequest, NextResponse } from "next/server";
-import { z } from "zod";
-import { env } from "@/env";
-import { baseRateLimit } from "@/lib/rate-limit";
-import { isTranscriptionConfigured } from "@/lib/transcription-utils";
-
-const transcribeRequestSchema = z.object({
- filename: z.string().min(1, "Filename is required"),
- language: z.string().optional().default("auto"),
- decryptionKey: z.string().min(1, "Decryption key is required").optional(),
- iv: z.string().min(1, "IV is required").optional(),
-});
-
-const modalResponseSchema = z.object({
- text: z.string(),
- segments: z.array(
- z.object({
- id: z.number(),
- seek: z.number(),
- start: z.number(),
- end: z.number(),
- text: z.string(),
- tokens: z.array(z.number()),
- temperature: z.number(),
- avg_logprob: z.number(),
- compression_ratio: z.number(),
- no_speech_prob: z.number(),
- })
- ),
- language: z.string(),
-});
-
-const apiResponseSchema = z.object({
- text: z.string(),
- segments: z.array(
- z.object({
- id: z.number(),
- seek: z.number(),
- start: z.number(),
- end: z.number(),
- text: z.string(),
- tokens: z.array(z.number()),
- temperature: z.number(),
- avg_logprob: z.number(),
- compression_ratio: z.number(),
- no_speech_prob: z.number(),
- })
- ),
- language: z.string(),
-});
-
-export async function POST(request: NextRequest) {
- try {
- // Rate limiting
- const ip = request.headers.get("x-forwarded-for") ?? "anonymous";
- const { success } = await baseRateLimit.limit(ip);
- const origin = request.headers.get("origin");
-
- if (!success) {
- return NextResponse.json({ error: "Too many requests" }, { status: 429 });
- }
-
- // Check transcription configuration
- const transcriptionCheck = isTranscriptionConfigured();
- if (!transcriptionCheck.configured) {
- console.error(
- "Missing environment variables:",
- JSON.stringify(transcriptionCheck.missingVars)
- );
-
- return NextResponse.json(
- {
- error: "Transcription not configured",
- message: `Auto-captions require environment variables: ${transcriptionCheck.missingVars.join(", ")}. Check README for setup instructions.`,
- },
- { status: 503 }
- );
- }
-
- // Parse and validate request body
- const rawBody = await request.json().catch(() => null);
- if (!rawBody) {
- return NextResponse.json(
- { error: "Invalid JSON in request body" },
- { status: 400 }
- );
- }
-
- const validationResult = transcribeRequestSchema.safeParse(rawBody);
- if (!validationResult.success) {
- return NextResponse.json(
- {
- error: "Invalid request parameters",
- details: validationResult.error.flatten().fieldErrors,
- },
- { status: 400 }
- );
- }
-
- const { filename, language, decryptionKey, iv } = validationResult.data;
-
- // Prepare request body for Modal
- const modalRequestBody: any = {
- filename,
- language,
- };
-
- // Add encryption parameters if provided (zero-knowledge)
- if (decryptionKey && iv) {
- modalRequestBody.decryptionKey = decryptionKey;
- modalRequestBody.iv = iv;
- }
-
- // Call Modal transcription service
- const response = await fetch(env.MODAL_TRANSCRIPTION_URL, {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
- body: JSON.stringify(modalRequestBody),
- });
-
- if (!response.ok) {
- const errorText = await response.text();
- console.error("Modal API error:", response.status, errorText);
-
- let errorMessage = "Transcription service unavailable";
- try {
- const errorData = JSON.parse(errorText);
- errorMessage = errorData.error || errorMessage;
- } catch {
- // Use default message if parsing fails
- }
-
- return NextResponse.json(
- {
- error: errorMessage,
- message: "Failed to process transcription request",
- },
- { status: response.status >= 500 ? 502 : response.status }
- );
- }
-
- const rawResult = await response.json();
- console.log("Raw Modal response:", JSON.stringify(rawResult, null, 2));
-
- // Validate Modal response
- const modalValidation = modalResponseSchema.safeParse(rawResult);
- if (!modalValidation.success) {
- console.error("Invalid Modal API response:", modalValidation.error);
- return NextResponse.json(
- { error: "Invalid response from transcription service" },
- { status: 502 }
- );
- }
-
- const result = modalValidation.data;
-
- // Prepare and validate API response
- const responseData = {
- text: result.text,
- segments: result.segments,
- language: result.language,
- };
-
- const responseValidation = apiResponseSchema.safeParse(responseData);
- if (!responseValidation.success) {
- console.error(
- "Invalid API response structure:",
- responseValidation.error
- );
- return NextResponse.json(
- { error: "Internal response formatting error" },
- { status: 500 }
- );
- }
-
- return NextResponse.json(responseValidation.data);
- } catch (error) {
- console.error("Transcription API error:", error);
- return NextResponse.json(
- {
- error: "Internal server error",
- message: "An unexpected error occurred during transcription",
- },
- { status: 500 }
- );
- }
-}