feat: 将视频转写重构为纯文字异步语音识别

This commit is contained in:
2026-10-03 21:58:06 +08:00
parent 040273586b
commit 6f0e38054b
34 changed files with 2283 additions and 1771 deletions
+65
View File
@@ -0,0 +1,65 @@
import assert from "node:assert/strict";
import { execFileSync } from "node:child_process";
import { mkdtemp, rm, writeFile } from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { test } from "node:test";
import { extractAudio } from "../app/api/media";
test("extract a whole video longer than five minutes as compact mono MP3", async () => {
const dir = await mkdtemp(path.join(os.tmpdir(), "asr-media-test-"));
try {
const video = path.join(dir, "long.mp4");
execFileSync(process.env.FFMPEG_PATH || "ffmpeg", [
"-v",
"error",
"-f",
"lavfi",
"-i",
"color=c=black:s=16x16:r=1",
"-f",
"lavfi",
"-i",
"sine=frequency=440:sample_rate=16000",
"-t",
"310",
"-c:v",
"libx264",
"-preset",
"ultrafast",
"-c:a",
"aac",
video,
]);
const result = await extractAudio(video, {
format: "mp3",
bitrateKbps: 64,
});
assert.ok(result);
assert.ok(result.buffer.length < 3_000_000);
const audio = path.join(dir, "audio.mp3");
await writeFile(audio, result.buffer);
const probe = JSON.parse(
execFileSync(
process.env.FFPROBE_PATH || "ffprobe",
[
"-v",
"error",
"-show_entries",
"stream=codec_name,sample_rate,channels:format=duration",
"-of",
"json",
audio,
],
{ encoding: "utf8" },
),
);
assert.equal(probe.streams[0].codec_name, "mp3");
assert.equal(probe.streams[0].sample_rate, "16000");
assert.equal(probe.streams[0].channels, 1);
assert.ok(Number(probe.format.duration) >= 310);
assert.ok(Number(probe.format.duration) < 311);
} finally {
await rm(dir, { force: true, recursive: true });
}
});