import { db } from "../../../../db/index"; import { lessons } from "../../../../db/schema"; import { eq } from "drizzle-orm"; import { generateStepTTS, generateQuestionTTS, generateOptionTTS, } from "../../../../utils/generateTTS"; import { verifyLicenseKey } from "../../../../utils/license"; export default defineEventHandler(async (event) => { const key = getHeader(event, "x-license-key") ?? ""; if (!verifyLicenseKey(key)) { throw createError({ statusCode: 401, message: "Unauthorized" }); } const topicId = getRouterParam(event, "id")!; const lesson = await db.query.lessons.findFirst({ where: eq(lessons.topicId, topicId) }); if (!lesson) throw createError({ statusCode: 404, message: "Lesson not found" }); const content = JSON.parse(lesson.content); const steps: any[] = content.steps ?? []; type Task = () => Promise; const tasks: Task[] = []; for (let si = 0; si < steps.length; si++) { const step = steps[si]; const siCopy = si; if (step.type === "concept" || step.type === "example") { const text = [step.body, step.callout].filter(Boolean).join(" "); if (!text.trim()) continue; tasks.push(async () => { const r = await generateStepTTS(text, lesson.id, siCopy); if (r) { step.audioPath = r.audioPath; step.audioChunks = r.audioChunks; } }); } else if (step.type === "summary") { const text = Array.isArray(step.bullets) ? step.bullets.join(". ") : ""; if (!text.trim()) continue; tasks.push(async () => { const r = await generateStepTTS(text, lesson.id, siCopy); if (r) { step.audioPath = r.audioPath; step.audioChunks = r.audioChunks; } }); } else if (step.type === "question") { if (step.body?.trim()) { tasks.push(async () => { const r = await generateQuestionTTS(step.body, lesson.id, siCopy); if (r) { step.questionAudioPath = r.audioPath; step.questionAudioChunks = r.audioChunks; } }); } if (Array.isArray(step.options)) { step.optionAudioPaths = new Array(step.options.length).fill(null); for (let oi = 0; oi < step.options.length; oi++) { const optText = step.options[oi]; const oiCopy = oi; if (optText?.trim()) { tasks.push(async () => { const r = await generateOptionTTS(optText, lesson.id, siCopy, oiCopy); if (r) step.optionAudioPaths[oiCopy] = r.audioPath; }); } } } } } const BATCH = 3; for (let i = 0; i < tasks.length; i += BATCH) { await Promise.all(tasks.slice(i, i + BATCH).map((fn) => fn())); } content.steps = steps; await db.update(lessons).set({ content: JSON.stringify(content) }).where(eq(lessons.id, lesson.id)); return { status: "ready" }; });