Update version to 0.4.9, enhance README with Trendshift badge, and add new embedding models to providerModels.js. Refactor TTS handling to support additional providers and improve API key validation for media providers.

This commit is contained in:
decolua committed 2026-04-29 11:34:39 +07:00
1 parent e8aa5e2222
commit 512e3de371
20 files changed
+586 -83

No files matched your search

@@ -0,0 +1,65 @@
import { NextResponse } from "next/server";
import { getProviderConnections } from "@/lib/localDb";
const langNames = new Intl.DisplayNames(["en"], { type: "language" });
/**
* GET /api/media-providers/tts/deepgram/voices[?lang=en]
* Returns { languages, byLang } grouped by language code (same shape as edge-tts/elevenlabs/inworld)
* Each Deepgram voice = one model (canonical_name like "aura-2-thalia-en")
*/
export async function GET(request) {
try {
const { searchParams } = new URL(request.url);
const langFilter = searchParams.get("lang");
const connections = await getProviderConnections({ provider: "deepgram", isActive: true });
const apiKey = connections[0]?.apiKey;
if (!apiKey) return NextResponse.json({ error: "No Deepgram connection found" }, { status: 400 });
const res = await fetch("https://api.deepgram.com/v1/models", {
headers: { "Authorization": `Token ${apiKey}` },
});
if (!res.ok) {
const text = await res.text().catch(() => "");
return NextResponse.json({ error: `Deepgram API ${res.status}: ${text || "Failed"}` }, { status: 502 });
}
const data = await res.json();
const ttsModels = data.tts || [];
const byLang = {};
for (const m of ttsModels) {
// Deepgram returns `languages: ["en"]` or sometimes language inferred from canonical_name suffix
const langs = Array.isArray(m.languages) && m.languages.length
? m.languages
: [m.canonical_name?.split("-").pop() || "en"];
for (const code of langs) {
if (!byLang[code]) {
byLang[code] = {
code,
name: (() => { try { return langNames.of(code); } catch { return code; } })(),
voices: [],
};
}
const voiceId = m.canonical_name || m.name;
if (!byLang[code].voices.find((x) => x.id === voiceId)) {
byLang[code].voices.push({
id: voiceId,
name: m.name || voiceId,
gender: m.metadata?.tags?.find((t) => t === "masculine" || t === "feminine") || "",
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 });
}
}
@@ -0,0 +1,61 @@
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 });
}
}