83 lines
2.8 KiB
TypeScript
83 lines
2.8 KiB
TypeScript
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<void>;
|
|
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" };
|
|
});
|