import fs from "node:fs"; import type { IncomingMessage, ServerResponse } from "node:http"; import path from "node:path"; import type { Connect, Plugin, ViteDevServer } from "vite"; import { spawn } from "child_process"; export function gttsPlugin(): Plugin { return { name: "gtts", configureServer(server: ViteDevServer) { // Vite dev server middlewares are connect-style: (req, res, next) server.middlewares.use(async (req, res, next) => { const reqUrl = req.url; if (!reqUrl) return next(); const url = new URL(reqUrl, "http://localhost"); if (url.pathname !== "/api/tts") return next(); const text = url.searchParams.get("text"); if (!text) return next(); try { await gtts(text, req, res, next); } catch (e) { console.error(e, reqUrl); next(); } }); }, }; } let fileList: Record = {}; async function gtts( text: string, req: Connect.IncomingMessage, res: ServerResponse, next: Connect.NextFunction, ) { text = text.replace(/["]/g, " "); const saveroot = "docs/.vitepress/cache/gtts"; fs.mkdirSync(saveroot, { recursive: true }); const fileListJSON = path.join(saveroot, "gtts.json"); if (Object.keys(fileList).length === 0 && fs.existsSync(fileListJSON)) { fileList = JSON.parse(fs.readFileSync(fileListJSON, "utf-8")); } const fileNo = fileList[text] || Object.keys(fileList).length; const filename = `${fileNo}.mp3`; const saveto = path.join(saveroot, filename); if (!fs.existsSync(saveto)) { await new Promise((resolve, reject) => { const p = spawn( "gtts-cli", [ `"${text}"`, ...["--lang", "ko"], ...["--output", path.join(process.cwd(), saveto)], ], { stdio: "inherit", shell: true, }, ); p.on("error", reject).on("close", resolve); }); fileList[text] = fileNo; console.log(`gtts: ${text} saved to ${saveto}`); fs.writeFileSync(fileListJSON, JSON.stringify(fileList, null, 2)); } else { // console.log(`gtts: ${text} loaded from ${saveto}`); } res.setHeader("Content-Type", "audio/mpeg"); fs.createReadStream(saveto).pipe(res); }