src/app/api/generate/route.ts (100 lines of code) (raw):

import { NextRequest, userAgent } from "next/server"; export const MAX_INPUT_LENGTH = 1000; export const MAX_PROMPT_LENGTH = 1000; // GET handler that proxies requests to the OpenAI TTS API and streams // the response back to the client. import { VOICES } from "@/lib/library"; export async function GET(req: NextRequest) { const { searchParams } = new URL(req.url); const ua = userAgent(req); const response_format = ua.engine?.name === "Blink" ? "wav" : "mp3"; // Get parameters from the query string let input = searchParams.get("input") || ""; let prompt = searchParams.get("prompt") || ""; const voice = searchParams.get("voice") || ""; const vibe = searchParams.get("vibe") || "audio"; // Truncate input and prompt to max 1000 characters // Frontend handles this, but we'll do it here too // to avoid extra requests to the server input = input.slice(0, MAX_INPUT_LENGTH); prompt = prompt.slice(0, MAX_PROMPT_LENGTH); if (!VOICES.includes(voice)) { return new Response("Invalid voice", { status: 400 }); } try { const apiResponse = await fetch("https://api.openai.com/v1/audio/speech", { method: "POST", headers: { Authorization: `Bearer ${process.env.OPENAI_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ model: "gpt-4o-mini-tts", input, response_format, voice, // Don't pass if empty ...(prompt && { instructions: prompt }), }), }); if (!apiResponse.ok) { return new Response(`An error occurred while generating the audio.`, { status: apiResponse.status, }); } const filename = `openai-fm-${voice}-${vibe}.${response_format}`; // Stream response back to client. return new Response(apiResponse.body, { headers: { "Content-Type": response_format === "wav" ? "audio/wav" : "audio/mpeg", "Content-Disposition": `inline; filename="${filename}"`, "Cache-Control": "no-cache", }, }); } catch (err) { console.error("Error generating speech:", err); return new Response("Error generating speech", { status: 500, }); } } export async function POST(req: NextRequest) { const ua = userAgent(req); const response_format = ua.engine?.name === "Blink" ? "wav" : "mp3"; const formData = await req.formData(); let input = formData.get("input")?.toString() || ""; let prompt = formData.get("prompt")?.toString() || ""; const voice = formData.get("voice")?.toString() || ""; const vibe = formData.get("vibe") || "audio"; // Truncate input and prompt to max 1000 characters // Frontend handles this, but we'll do it here too // to avoid extra requests to the server input = input.slice(0, MAX_INPUT_LENGTH); prompt = prompt.slice(0, MAX_PROMPT_LENGTH); if (!VOICES.includes(voice)) { return new Response("Invalid voice", { status: 400 }); } try { const apiResponse = await fetch("https://api.openai.com/v1/audio/speech", { method: "POST", headers: { Authorization: `Bearer ${process.env.OPENAI_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ model: "gpt-4o-mini-tts", input, response_format, voice, // Don't pass if empty ...(prompt && { instructions: prompt }), }), }); if (!apiResponse.ok) { return new Response(`An error occurred while generating the audio.`, { status: apiResponse.status, }); } const filename = `openai-fm-${voice}-${vibe}.${response_format}`; // Stream response back to client. return new Response(apiResponse.body, { headers: { "Content-Type": response_format === "wav" ? "audio/wav" : "audio/mpeg", "Content-Disposition": `inline; filename="${filename}"`, "Cache-Control": "no-cache", }, }); } catch (err) { console.error("Error generating speech:", err); return new Response("Error generating speech", { status: 500, }); } }