feat(audio): add downloadable extra Piper voices with accents

Adds Alma (sv), Alan, Northern English, Cori, Alba (British/Scottish)
and Joe, John, Norman (American) as optional voices downloaded one at
a time from Settings, with gender (measured pitch) and accent shown in
the voice lists. The standard install is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
larssand
2026-09-30 21:58:08 +02:00
parent aaca5e8c63
commit ebd0f74f40
15 changed files with 186 additions and 25 deletions

View File

@@ -10,7 +10,7 @@ const publicTrainingProfile = require("./server/public_training_profile");
const { createBackupService } = require("./server/backup_service");
const { createAuth, COOKIE_NAME: AUTH_COOKIE_NAME, TOKEN_MAX_AGE_MS } = require("./server/auth");
const { createTts, resolvePiperBinary } = require("./server/tts");
const { installAll: installPiperAll } = require("./server/tts_install");
const { installAll: installPiperAll, VOICES: PIPER_VOICES } = require("./server/tts_install");
const PORT = Number(process.env.PORT || 8081);
const HOST = process.env.HOST || "0.0.0.0";
@@ -220,13 +220,16 @@ app.post("/api/tts/speech", async (req, res) => {
}
});
// One-time download of Piper + Swedish/English voices, run in the background; progress is
// polled through /api/tts/status. Needs internet only while it runs.
const ttsInstall = { running: false, log: [], error: "" };
app.post("/api/tts/install", (_req, res) => {
// One-time download of Piper + Swedish/English voices, or of one extra catalog voice
// ({ voice }), run in the background; progress is polled through /api/tts/status. Needs
// internet only while it runs.
const ttsInstall = { running: false, log: [], error: "", voice: "" };
app.post("/api/tts/install", (req, res) => {
const voice = String(req.body?.voice || "");
if (voice && !PIPER_VOICES.some((item) => item.name === voice)) return res.status(400).json({ error: "Unknown voice" });
if (!ttsInstall.running) {
Object.assign(ttsInstall, { running: true, log: [], error: "" });
installPiperAll({ projectDir: __dirname, hasBinary: Boolean(resolvePiperBinary(process.env.RC_PIPER_BIN || "", __dirname)), log: (line) => ttsInstall.log.push(line) })
Object.assign(ttsInstall, { running: true, log: [], error: "", voice });
installPiperAll({ projectDir: __dirname, voice, hasBinary: Boolean(resolvePiperBinary(process.env.RC_PIPER_BIN || "", __dirname)), log: (line) => ttsInstall.log.push(line) })
.catch((error) => { ttsInstall.error = error.message; })
.finally(() => { ttsInstall.running = false; });
}