mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-13 07:40:06 +00:00
fix(media-use): create the output dir before ElevenLabs TTS writes
The ElevenLabs provider spawns a Python helper that writes straight to wavAbs
via a bare open(), which (unlike heygen/kokoro) never creates the parent dir —
so on a fresh project the save throws ENOENT and the line is silently dropped
as 'TTS failed - omitted'. mkdir -p the dir first, guarded so a mkdir failure
(EACCES/EROFS) returns { ok:false } like the rest of the branch rather than
throwing. (Migrated from #1960, whose skills/hyperframes-media path was retired
into skills/media-use; the bug moved with it.)
This commit is contained in:
@@ -46,7 +46,7 @@
|
|||||||
"files": 10
|
"files": 10
|
||||||
},
|
},
|
||||||
"media-use": {
|
"media-use": {
|
||||||
"hash": "d28c7979e727a6a9",
|
"hash": "49352d2f3b42c0ba",
|
||||||
"files": 101
|
"files": 101
|
||||||
},
|
},
|
||||||
"motion-graphics": {
|
"motion-graphics": {
|
||||||
|
|||||||
@@ -239,6 +239,18 @@ export async function synthesizeOne({
|
|||||||
}) {
|
}) {
|
||||||
if (provider === "heygen") return synthesizeHeygen({ text, voiceId, lang, speed, wavAbs });
|
if (provider === "heygen") return synthesizeHeygen({ text, voiceId, lang, speed, wavAbs });
|
||||||
if (provider === "elevenlabs") {
|
if (provider === "elevenlabs") {
|
||||||
|
// The Python helper writes straight to wavAbs; unlike heygen (transcodeToWav)
|
||||||
|
// and kokoro (the `hyperframes tts` CLI), it does NOT create the parent dir,
|
||||||
|
// so on a fresh project (no assets/voice/ yet) the save fails and the line is
|
||||||
|
// silently dropped as "TTS failed - omitted". Create it first, like the other
|
||||||
|
// providers do. Guarded so a mkdir failure (EACCES/EROFS) returns
|
||||||
|
// { ok:false } like the rest of this branch rather than throwing (the
|
||||||
|
// function's contract is "never throws; failures return { ok:false }").
|
||||||
|
try {
|
||||||
|
mkdirSync(dirname(wavAbs), { recursive: true });
|
||||||
|
} catch {
|
||||||
|
return { ok: false, words: null };
|
||||||
|
}
|
||||||
const { cmd, args } = pythonInvocation([
|
const { cmd, args } = pythonInvocation([
|
||||||
"-c",
|
"-c",
|
||||||
ELEVENLABS_PY,
|
ELEVENLABS_PY,
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { test } from "node:test";
|
import { test } from "node:test";
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
import { mkdtempSync, writeFileSync, chmodSync, rmSync } from "node:fs";
|
import { mkdtempSync, writeFileSync, chmodSync, rmSync, existsSync } from "node:fs";
|
||||||
import { join } from "node:path";
|
import { join, dirname } from "node:path";
|
||||||
import { tmpdir } from "node:os";
|
import { tmpdir } from "node:os";
|
||||||
import { parseFfmpegDurationBanner, ffprobeDuration } from "./tts.mjs";
|
import { parseFfmpegDurationBanner, ffprobeDuration, synthesizeOne } from "./tts.mjs";
|
||||||
|
|
||||||
test("parseFfmpegDurationBanner reads ffmpeg's stderr Duration line", () => {
|
test("parseFfmpegDurationBanner reads ffmpeg's stderr Duration line", () => {
|
||||||
const stderr = [
|
const stderr = [
|
||||||
@@ -64,3 +64,26 @@ test("ffprobeDuration returns NaN when neither ffprobe nor ffmpeg resolve", () =
|
|||||||
rmSync(dir, { recursive: true, force: true });
|
rmSync(dir, { recursive: true, force: true });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("synthesizeOne(elevenlabs) creates the output dir before writing", async () => {
|
||||||
|
const dir = mkdtempSync(join(tmpdir(), "tts-el-mkdir-"));
|
||||||
|
const wavAbs = join(dir, "assets", "voice", "line-0.wav"); // nested, not yet created
|
||||||
|
const savedKey = process.env.ELEVENLABS_API_KEY;
|
||||||
|
try {
|
||||||
|
// Unset the key so the Python side fails fast — the mkdir must run before
|
||||||
|
// the spawn regardless, which is what this guards.
|
||||||
|
delete process.env.ELEVENLABS_API_KEY;
|
||||||
|
await synthesizeOne({
|
||||||
|
provider: "elevenlabs",
|
||||||
|
text: "hi",
|
||||||
|
voiceId: "v",
|
||||||
|
wavAbs,
|
||||||
|
hyperframesDir: dir,
|
||||||
|
});
|
||||||
|
assert.ok(existsSync(dirname(wavAbs)), "output directory should be created");
|
||||||
|
} finally {
|
||||||
|
if (savedKey === undefined) delete process.env.ELEVENLABS_API_KEY;
|
||||||
|
else process.env.ELEVENLABS_API_KEY = savedKey;
|
||||||
|
rmSync(dir, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user