Add local Whisper speech input

This commit is contained in:
Ismail Ali
2026-04-28 16:35:59 +02:00
parent ec69117e6f
commit d54aae7bac
10 changed files with 614 additions and 7 deletions

88
tools/stt-server.mjs Normal file
View File

@@ -0,0 +1,88 @@
import fs from 'node:fs';
import path from 'node:path';
import cors from 'cors';
import express from 'express';
import multer from 'multer';
import { nodewhisper } from 'nodejs-whisper';
const app = express();
const port = Number(process.env.STT_PORT ?? 3334);
const host = process.env.STT_HOST ?? '0.0.0.0';
const uploadDir = path.resolve(process.cwd(), '.stt-uploads');
const modelRootPath = path.resolve(process.cwd(), '.whisper-models');
const modelName = process.env.STT_MODEL ?? 'tiny.en';
fs.mkdirSync(uploadDir, { recursive: true });
fs.mkdirSync(modelRootPath, { recursive: true });
const upload = multer({ dest: uploadDir });
app.use(cors());
app.get('/health', (_request, response) => {
response.json({ ok: true, model: modelName });
});
app.post('/transcribe', upload.single('audio'), async (request, response) => {
if (!request.file) {
response.status(400).json({ error: 'Missing audio file' });
return;
}
try {
const text = await nodewhisper(request.file.path, {
modelName,
autoDownloadModelName: modelName,
modelRootPath,
removeWavFileAfterTranscription: true,
withCuda: false,
logger: quietLogger,
whisperOptions: {
outputInText: false,
outputInSrt: false,
outputInVtt: false,
outputInCsv: false,
outputInJson: false,
outputInJsonFull: false,
outputInLrc: false,
outputInWords: false,
translateToEnglish: false,
splitOnWord: true,
noGpu: true,
},
});
response.json({ text: cleanTranscript(text) });
} catch (error) {
response.status(500).json({ error: error instanceof Error ? error.message : String(error) });
} finally {
safeUnlink(request.file.path);
}
});
app.listen(port, host, () => {
console.log(`STT server listening on http://${host}:${port}`);
console.log(`Whisper model: ${modelName}`);
});
const quietLogger = {
debug: () => undefined,
info: () => undefined,
log: () => undefined,
warn: console.warn,
error: console.error,
};
function cleanTranscript(value) {
return String(value)
.replace(/\[[^\]]*\]/g, ' ')
.replace(/\([^)]*\)/g, ' ')
.replace(/\s+/g, ' ')
.trim();
}
function safeUnlink(filePath) {
if (filePath && fs.existsSync(filePath)) {
fs.unlinkSync(filePath);
}
}