Stimmen-Skript auf offizielles ElevenLabs-SDK und eleven_v4 umgestellt (Sprache je Aufnahme, Schlüssel aus .env.local)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018MyLusGH2U5AudjoARey3N
This commit is contained in:
parent
1db1001dfc
commit
5091887695
4 changed files with 109 additions and 10 deletions
84
remotion/package-lock.json
generated
84
remotion/package-lock.json
generated
|
|
@ -6,6 +6,7 @@
|
|||
"": {
|
||||
"name": "pino-remotion",
|
||||
"dependencies": {
|
||||
"@elevenlabs/elevenlabs-js": "^2.75.0",
|
||||
"@remotion/cli": "^4.0.300",
|
||||
"@remotion/google-fonts": "^4.0.300",
|
||||
"react": "^18.3.1",
|
||||
|
|
@ -32,6 +33,41 @@
|
|||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@elevenlabs/elevenlabs-js": {
|
||||
"version": "2.75.0",
|
||||
"resolved": "https://registry.npmjs.org/@elevenlabs/elevenlabs-js/-/elevenlabs-js-2.75.0.tgz",
|
||||
"integrity": "sha512-sCEddZAQS+JdAvD+jnqXund75mygRkHzgigqbWsQpcJozwe15FOtymS2+28YMOY06Pmja9l8EUE1LlXAtlW0Nw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"command-exists": "^1.2.9",
|
||||
"node-fetch": "^2.7.0",
|
||||
"ws": "^8.18.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@elevenlabs/elevenlabs-js/node_modules/ws": {
|
||||
"version": "8.22.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.22.1.tgz",
|
||||
"integrity": "sha512-oyL6OPYme/CmiFvNuGzeoxCJ4nOrZE2O1FwM9MWM1AsGri6nLXB2pg+lNA8aLeXMSav0J6eIVS1j6vUPzUtK9g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": ">=5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz",
|
||||
|
|
@ -1276,6 +1312,12 @@
|
|||
"node": ">=6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/command-exists": {
|
||||
"version": "1.2.9",
|
||||
"resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz",
|
||||
"integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/commander": {
|
||||
"version": "2.20.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
|
|
@ -2117,6 +2159,26 @@
|
|||
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
||||
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "4.x || >=6.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"encoding": "^0.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"encoding": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/node-releases": {
|
||||
"version": "2.0.60",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.60.tgz",
|
||||
|
|
@ -2745,6 +2807,12 @@
|
|||
"integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
|
|
@ -3295,6 +3363,12 @@
|
|||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/webidl-conversions": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
|
||||
"license": "BSD-2-Clause"
|
||||
},
|
||||
"node_modules/webpack": {
|
||||
"version": "5.112.2",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.112.2.tgz",
|
||||
|
|
@ -3413,6 +3487,16 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/whatwg-url": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tr46": "~0.0.3",
|
||||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
"render": "remotion render"
|
||||
},
|
||||
"dependencies": {
|
||||
"@elevenlabs/elevenlabs-js": "^2.75.0",
|
||||
"@remotion/cli": "^4.0.300",
|
||||
"@remotion/google-fonts": "^4.0.300",
|
||||
"react": "^18.3.1",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
{
|
||||
"modell": "eleven_multilingual_v2",
|
||||
"modell": "eleven_v4",
|
||||
"format": "mp3_44100_128",
|
||||
"einstellungen": {"stability": 0.5, "similarity_boost": 0.75, "style": 0.2},
|
||||
"einstellungen": {
|
||||
"stability": 0.5,
|
||||
"similarity_boost": 0.75
|
||||
},
|
||||
"stimmen": {
|
||||
"ERZ": "",
|
||||
"PINO": "",
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@
|
|||
// … F02 --probe nur anzeigen, was erzeugt würde (kostet nichts)
|
||||
// … F02 --sprache es andere Sprache (de, es, fr; ohne Angabe de)
|
||||
//
|
||||
// Der Schlüssel kommt nur aus der Umgebungsvariable, nie aus einer Datei im Repo.
|
||||
// Der Schlüssel kommt aus ELEVENLABS_API_KEY bzw. aus youtube/.env.local (angelegt mit
|
||||
// `npx -y @elevenlabs/cli@latest onboard init`, nicht im Git) – nie aus dem Code.
|
||||
// Stimmen-IDs und Modell stehen in werkzeuge/stimmen.config.json (je Sprache
|
||||
// lassen sich dort unter „sprachen“ eigene Stimmen eintragen).
|
||||
// Aufnahmen landen in public/stimmen/folgeNN/<sprache>/ (nicht im Git; auf dem VPS ein
|
||||
|
|
@ -19,9 +20,14 @@ import {spawnSync} from 'node:child_process';
|
|||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import {fileURLToPath} from 'node:url';
|
||||
import {Readable} from 'node:stream';
|
||||
import {ElevenLabsClient} from '@elevenlabs/elevenlabs-js';
|
||||
|
||||
const WURZEL = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
||||
process.chdir(WURZEL);
|
||||
// Schlüssel aus der .env.local im Repo-Wurzelordner laden (falls nicht schon gesetzt)
|
||||
const envDatei = path.join(WURZEL, '..', '.env.local');
|
||||
if (!process.env.ELEVENLABS_API_KEY && fs.existsSync(envDatei)) process.loadEnvFile(envDatei);
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
const kuerzel = args.find((a) => !a.startsWith('--'));
|
||||
|
|
@ -42,7 +48,8 @@ const cfg = JSON.parse(fs.readFileSync('werkzeuge/stimmen.config.json', 'utf8'))
|
|||
einstellungen?: Record<string, unknown>;
|
||||
};
|
||||
const schluessel = process.env.ELEVENLABS_API_KEY;
|
||||
if (!schluessel && !probe) throw new Error('ELEVENLABS_API_KEY ist nicht gesetzt.');
|
||||
if (!schluessel && !probe) throw new Error('ELEVENLABS_API_KEY ist nicht gesetzt (siehe youtube/.env.local).');
|
||||
const client = probe ? null : new ElevenLabsClient(); // liest ELEVENLABS_API_KEY
|
||||
|
||||
const npx = process.platform === 'win32' ? 'npx.cmd' : 'npx';
|
||||
const ausgabe = spawnSync(npx, ['tsx', 'werkzeuge/szenenliste.mts', `./src/folge${nummer}/Folge${nummer}.tsx`, kuerzel], {encoding: 'utf8', shell: process.platform === 'win32', env: {...process.env, SPRACHE: sprache}});
|
||||
|
|
@ -91,13 +98,17 @@ for (const s of szenen) {
|
|||
console.log(`~ ${schl} (${z.wer}): ${z.text}`);
|
||||
continue;
|
||||
}
|
||||
const antwort = await fetch(`https://api.elevenlabs.io/v1/text-to-speech/${stimme}?output_format=${cfg.format}`, {
|
||||
method: 'POST',
|
||||
headers: {'xi-api-key': schluessel!, 'Content-Type': 'application/json', Accept: 'audio/mpeg'},
|
||||
body: JSON.stringify({text: z.text, model_id: cfg.modell, voice_settings: cfg.einstellungen}),
|
||||
// Sprache festlegen (bessere Aussprache); eleven_v4 kennt nur stability und similarity_boost
|
||||
const audio = await client!.textToSpeech.convert(stimme, {
|
||||
text: z.text,
|
||||
modelId: cfg.modell,
|
||||
languageCode: sprache,
|
||||
outputFormat: cfg.format as never,
|
||||
voiceSettings: {stability: cfg.einstellungen?.stability as number, similarityBoost: cfg.einstellungen?.similarity_boost as number},
|
||||
});
|
||||
if (!antwort.ok) throw new Error(`${schl}: ElevenLabs antwortet ${antwort.status} ${await antwort.text()}`);
|
||||
fs.writeFileSync(pfad, Buffer.from(await antwort.arrayBuffer()));
|
||||
const teile: Buffer[] = [];
|
||||
for await (const t of Readable.fromWeb(audio as never)) teile.push(Buffer.from(t));
|
||||
fs.writeFileSync(pfad, Buffer.concat(teile));
|
||||
aufnahmen[schl] = {datei, dauer: laenge(pfad), text: z.text};
|
||||
fs.writeFileSync(jsonDatei, JSON.stringify(aufnahmen, null, '\t') + '\n');
|
||||
erzeugt++;
|
||||
|
|
|
|||
Loading…
Reference in a new issue