feat: Puente Mayúscula-Minúscula + TTS SpeechSynthesis + E2E fixes

This commit is contained in:
renato97
2026-07-21 15:31:46 -03:00
commit fa8e0c58ce
120 changed files with 16013 additions and 0 deletions
+49
View File
@@ -0,0 +1,49 @@
import { NextRequest, NextResponse } from "next/server"
import { execSync } from "child_process"
import { existsSync, mkdirSync } from "fs"
import { createHash } from "crypto"
import { readFile, mkdir } from "fs/promises"
import { join } from "path"
const CACHE_DIR = join(process.cwd(), "public", "tts-cache")
async function ensureCacheDir() {
if (!existsSync(CACHE_DIR)) {
await mkdir(CACHE_DIR, { recursive: true })
}
}
function getCachePath(text: string): string {
const hash = createHash("md5").update(text).digest("hex")
return join(CACHE_DIR, `${hash}.wav`)
}
export async function GET(request: NextRequest) {
const text = request.nextUrl.searchParams.get("text")
if (!text || text.length > 200) {
return NextResponse.json({ error: "text param required (max 200 chars)" }, { status: 400 })
}
await ensureCacheDir()
const cachePath = getCachePath(text)
if (!existsSync(cachePath)) {
try {
execSync(
`espeak-ng -v es-mx -s 140 -p 60 "${text.replace(/"/g, '\\"')}" -w "${cachePath}"`,
{ timeout: 10000 },
)
} catch {
return NextResponse.json({ error: "TTS generation failed" }, { status: 500 })
}
}
const audioBuffer = await readFile(cachePath)
return new NextResponse(audioBuffer, {
headers: {
"Content-Type": "audio/wav",
"Content-Length": audioBuffer.length.toString(),
"Cache-Control": "public, max-age=31536000, immutable",
},
})
}