initial commit
This commit is contained in:
209
generate-audio.js
Normal file
209
generate-audio.js
Normal file
@@ -0,0 +1,209 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Generate MP3 audio files for all German questions using ElevenLabs TTS API.
|
||||
*
|
||||
* Usage:
|
||||
* 1. Get your API key from https://elevenlabs.io/app/settings/api-keys
|
||||
* 2. Run: ELEVENLABS_API_KEY=your-key-here node generate-audio.js
|
||||
*
|
||||
* Output: src/public/audio/<topic_id>_<index>.mp3
|
||||
*
|
||||
* Free tier: ~10,000 characters/month — enough for all 60 questions (~2,500 chars total).
|
||||
*/
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const API_KEY = process.env.ELEVENLABS_API_KEY;
|
||||
if (!API_KEY) {
|
||||
console.error("Error: set ELEVENLABS_API_KEY environment variable");
|
||||
console.error(" ELEVENLABS_API_KEY=your-key node generate-audio.js");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Same questions as in index.html
|
||||
const TOPICS = [
|
||||
{
|
||||
id: "alltag",
|
||||
questions: [
|
||||
"Wie sieht dein typischer Morgen aus?",
|
||||
"Was machst du normalerweise in deiner Freizeit?",
|
||||
"Wie oft kochst du zu Hause und was bereitest du gerne zu?",
|
||||
"Beschreibe deinen Tagesablauf an einem Werktag.",
|
||||
"Welche Aufgaben im Haushalt magst du und welche nicht?",
|
||||
"Wie verbringst du deinen Feierabend?",
|
||||
"Machst du regelmäßig Sport? Was und wie oft?",
|
||||
"Wie oft triffst du dich mit Freunden oder Familie?",
|
||||
"Was liest du gerne – Bücher, Zeitungen oder Online-Artikel?",
|
||||
"Wie entspannst du dich nach einem stressigen Tag?",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "arbeit",
|
||||
questions: [
|
||||
"Was machst du beruflich und was gefällt dir an deiner Arbeit?",
|
||||
"Wie sieht dein Arbeitsplatz aus – Büro, Homeoffice oder beides?",
|
||||
"Was sind die größten Herausforderungen in deinem Job?",
|
||||
"Wie lange arbeitest du täglich und wie ist die Work-Life-Balance?",
|
||||
"Hast du nette Kollegen? Wie ist das Arbeitsklima?",
|
||||
"Welche Fähigkeiten brauchst du für deinen Beruf?",
|
||||
"Was würdest du beruflich ändern, wenn du könntest?",
|
||||
"Wie wichtig ist Karriere für dich im Vergleich zu Freizeit?",
|
||||
"Hast du schon mal den Job gewechselt? Warum?",
|
||||
"Was sind deine beruflichen Ziele für die Zukunft?",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "reisen",
|
||||
questions: [
|
||||
"Wohin bist du zuletzt gereist und wie war es?",
|
||||
"Reist du lieber alleine oder mit anderen? Warum?",
|
||||
"Was ist dein Traumreiseziel und warum?",
|
||||
"Bevorzugst du Strand, Berge oder Städtereisen?",
|
||||
"Wie planst du normalerweise deine Reisen?",
|
||||
"Was nimmst du immer mit auf Reisen?",
|
||||
"Hast du schon mal einen Kulturschock erlebt?",
|
||||
"Welches Essen aus einem anderen Land hat dir besonders gefallen?",
|
||||
"Wie wichtig ist es für dich, die Landessprache zu kennen?",
|
||||
"Erzähl von einem unvergesslichen Reiseerlebnis.",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "schweiz",
|
||||
questions: [
|
||||
"Was magst du am Leben in der Schweiz besonders?",
|
||||
"Wie unterscheidet sich das Schweizerdeutsch vom Hochdeutsch?",
|
||||
"Was sind typisch schweizerische Gewohnheiten oder Traditionen?",
|
||||
"Welche Sehenswürdigkeiten in Bern kennst du?",
|
||||
"Was ist das Besondere am schweizerischen Bildungssystem?",
|
||||
"Wie ist das öffentliche Verkehrsnetz in der Schweiz?",
|
||||
"Was denkst du über die Mehrsprachigkeit in der Schweiz?",
|
||||
"Welche Schweizer Gerichte kennst du und magst du?",
|
||||
"Wie erleben Ausländer das Einleben in der Schweiz?",
|
||||
"Was sind Vor- und Nachteile der direkten Demokratie?",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "gesundheit",
|
||||
questions: [
|
||||
"Wie wichtig ist dir deine Gesundheit im Alltag?",
|
||||
"Treibst du Sport für die Gesundheit oder zum Spaß?",
|
||||
"Wie ernährst du dich – bewusst oder eher spontan?",
|
||||
"Wie gehst du mit Stress um?",
|
||||
"Schläfst du gut? Was machst du für besseren Schlaf?",
|
||||
"Gehst du regelmäßig zum Arzt für Vorsorgeuntersuchungen?",
|
||||
"Was meinst du – wie viel Einfluss hat man auf die eigene Gesundheit?",
|
||||
"Wie beeinflusst das Wetter deine Stimmung und Gesundheit?",
|
||||
"Hast du Allergien oder besondere gesundheitliche Themen?",
|
||||
"Was ist für dich gesunde Ernährung?",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "meinungen",
|
||||
questions: [
|
||||
"Was denkst du über soziale Medien – Fluch oder Segen?",
|
||||
"Glaubst du, dass Homeoffice die Produktivität steigert?",
|
||||
"Was meinst du: Ist lebenslanges Lernen wichtig?",
|
||||
"Wie stehst du zu vegetarischer oder veganer Ernährung?",
|
||||
"Glaubst du, dass KI unsere Arbeitswelt stark verändern wird?",
|
||||
"Was hältst du von öffentlichem Nahverkehr statt Autofahren?",
|
||||
"Ist es wichtig, mehrere Sprachen zu sprechen? Warum?",
|
||||
"Was denkst du über die vier-Tage-Arbeitswoche?",
|
||||
"Sollten mehr Menschen ehrenamtlich arbeiten?",
|
||||
"Wie wichtig ist lokales Einkaufen für die Umwelt?",
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
// ElevenLabs voice ID — "Daniel" (German male, clear, natural)
|
||||
// Browse voices at https://elevenlabs.io/voice-library and replace if needed
|
||||
const VOICE_ID = "onwK4e9ZLuTAKqWW03F9";
|
||||
const MODEL_ID = "eleven_multilingual_v2";
|
||||
|
||||
const OUTPUT_DIR = path.join(__dirname, "src", "public", "audio");
|
||||
|
||||
async function generateAudio(text, outputPath) {
|
||||
const response = await fetch(
|
||||
`https://api.elevenlabs.io/v1/text-to-speech/${VOICE_ID}`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"xi-api-key": API_KEY,
|
||||
"Content-Type": "application/json",
|
||||
Accept: "audio/mpeg",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
text,
|
||||
model_id: MODEL_ID,
|
||||
language_code: "de",
|
||||
voice_settings: {
|
||||
stability: 0.6,
|
||||
similarity_boost: 0.8,
|
||||
speed: 0.9,
|
||||
},
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.text();
|
||||
throw new Error(`ElevenLabs API error ${response.status}: ${error}`);
|
||||
}
|
||||
|
||||
const buffer = Buffer.from(await response.arrayBuffer());
|
||||
fs.writeFileSync(outputPath, buffer);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
|
||||
|
||||
let total = 0;
|
||||
let generated = 0;
|
||||
let skipped = 0;
|
||||
|
||||
for (const topic of TOPICS) {
|
||||
total += topic.questions.length;
|
||||
}
|
||||
|
||||
console.log(`Generating ${total} audio files...\n`);
|
||||
|
||||
for (const topic of TOPICS) {
|
||||
for (let i = 0; i < topic.questions.length; i++) {
|
||||
const filename = `${topic.id}_${i}.mp3`;
|
||||
const outputPath = path.join(OUTPUT_DIR, filename);
|
||||
|
||||
// Skip if already exists (resume support)
|
||||
if (fs.existsSync(outputPath)) {
|
||||
skipped++;
|
||||
console.log(` [skip] ${filename} (already exists)`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const question = topic.questions[i];
|
||||
process.stdout.write(
|
||||
` [${generated + skipped + 1}/${total}] ${filename} ... `
|
||||
);
|
||||
|
||||
try {
|
||||
await generateAudio(question, outputPath);
|
||||
generated++;
|
||||
console.log("OK");
|
||||
} catch (err) {
|
||||
console.log(`FAILED: ${err.message}`);
|
||||
console.log(" Stopping. Re-run to continue from where you left off.");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Delay to respect rate limits
|
||||
await new Promise((r) => setTimeout(r, 500));
|
||||
}
|
||||
}
|
||||
|
||||
console.log(
|
||||
`\nDone! Generated: ${generated}, Skipped: ${skipped}, Total: ${total}`
|
||||
);
|
||||
console.log(`Files saved to: ${OUTPUT_DIR}`);
|
||||
}
|
||||
|
||||
main();
|
||||
Reference in New Issue
Block a user