Files
9router/src/app/api/media-providers/tts/inworld/voices/route.js
T

62 lines
2.2 KiB
JavaScript

import { NextResponse } from "next/server";
import { getProviderConnections } from "@/lib/localDb";
const langNames = new Intl.DisplayNames(["en"], { type: "language" });
/**
* GET /api/media-providers/tts/inworld/voices[?lang=en]
* Returns { languages, byLang } grouped by language code (same shape as edge-tts/elevenlabs)
*/
export async function GET(request) {
try {
const { searchParams } = new URL(request.url);
const langFilter = searchParams.get("lang");
const connections = await getProviderConnections({ provider: "inworld", isActive: true });
const apiKey = connections[0]?.apiKey;
if (!apiKey) return NextResponse.json({ error: "No Inworld connection found" }, { status: 400 });
const res = await fetch("https://api.inworld.ai/tts/v1/voices", {
headers: { "Authorization": `Basic ${apiKey}` },
});
if (!res.ok) {
const text = await res.text().catch(() => "");
return NextResponse.json({ error: `Inworld API ${res.status}: ${text || "Failed"}` }, { status: 502 });
}
const data = await res.json();
const voices = data.voices || [];
const byLang = {};
for (const v of voices) {
// Each voice has `languages: ["en", "es", ...]`
const langs = Array.isArray(v.languages) && v.languages.length ? v.languages : ["en"];
for (const code of langs) {
if (!byLang[code]) {
byLang[code] = {
code,
name: (() => { try { return langNames.of(code); } catch { return code; } })(),
voices: [],
};
}
if (!byLang[code].voices.find((x) => x.id === v.voiceId)) {
byLang[code].voices.push({
id: v.voiceId,
name: v.displayName || v.voiceId,
gender: v.gender || "",
lang: code,
});
}
}
}
const languages = Object.values(byLang).sort((a, b) => a.name.localeCompare(b.name));
if (langFilter) {
return NextResponse.json({ voices: byLang[langFilter]?.voices || [] });
}
return NextResponse.json({ languages, byLang });
} catch (err) {
return NextResponse.json({ error: err.message || "Failed to fetch voices" }, { status: 502 });
}
}