mirror of
https://github.com/webadderallorg/Recordly.git
synced 2026-09-25 07:16:02 +00:00
118 lines
3.2 KiB
TypeScript
118 lines
3.2 KiB
TypeScript
import { expect, it } from "vitest";
|
|
import { mergeCaptionSources } from "./mergeSources";
|
|
import { parseWhisperJsonCues } from "./parser";
|
|
|
|
it("keeps system speech when the microphone has no speech", () => {
|
|
const system = [{ id: "s", startMs: 0, endMs: 1000, text: "System speech" }];
|
|
expect(mergeCaptionSources([], system)[0].text).toBe("System speech");
|
|
});
|
|
it("keeps both tracks, choosing mic only for overlapping words", () => {
|
|
const mic = [{ id: "m", startMs: 1000, endMs: 2000, text: "Mic speech" }];
|
|
const system = [
|
|
{
|
|
id: "s",
|
|
startMs: 0,
|
|
endMs: 3000,
|
|
text: "Before conflict after",
|
|
words: [
|
|
{ text: "Before", startMs: 0, endMs: 900 },
|
|
{ text: "conflict", startMs: 1100, endMs: 1900, leadingSpace: true },
|
|
{ text: "after", startMs: 2100, endMs: 3000, leadingSpace: true },
|
|
],
|
|
},
|
|
];
|
|
expect(mergeCaptionSources(mic, system).map((cue) => cue.text)).toEqual([
|
|
"Before",
|
|
"Mic speech",
|
|
"after",
|
|
]);
|
|
});
|
|
it("retains real Whisper word timing through control tokens and punctuation", () => {
|
|
const cues = parseWhisperJsonCues(
|
|
JSON.stringify({
|
|
transcription: [
|
|
{
|
|
offsets: { from: 0, to: 1000 },
|
|
text: "Hello.",
|
|
tokens: [
|
|
{ text: "[_BEG_]", offsets: { from: 0, to: 0 } },
|
|
{ text: " Hello", offsets: { from: 0, to: 900 } },
|
|
{ text: ".", offsets: { from: 900, to: 900 } },
|
|
{ text: "[_TT_50]", offsets: { from: 1000, to: 1000 } },
|
|
],
|
|
},
|
|
],
|
|
}),
|
|
);
|
|
expect(cues[0].words).toEqual([{ text: "Hello.", startMs: 0, endMs: 900 }]);
|
|
});
|
|
|
|
it("does not let mic sound labels replace system speech", () => {
|
|
expect(
|
|
mergeCaptionSources(
|
|
[{ id: "m", startMs: 0, endMs: 2000, text: "[Coughing]" }],
|
|
[{ id: "s", startMs: 0, endMs: 2000, text: "The spoken paragraph." }],
|
|
).map((c) => c.text),
|
|
).toEqual(["The spoken paragraph."]);
|
|
});
|
|
|
|
it("preserves untimed speech next to timed words", async () => {
|
|
const { segmentCuesIntoPhrases } = await import("./segment");
|
|
const result = segmentCuesIntoPhrases(
|
|
[
|
|
{
|
|
id: "a",
|
|
startMs: 0,
|
|
endMs: 1000,
|
|
text: "First.",
|
|
words: [{ text: "First.", startMs: 0, endMs: 1000 }],
|
|
},
|
|
{ id: "b", startMs: 1000, endMs: 3000, text: "There is a timeline editor." },
|
|
],
|
|
[],
|
|
);
|
|
expect(result.map((c) => c.text).join(" ")).toContain("There is a timeline editor.");
|
|
});
|
|
|
|
it("preserves system speech between timed microphone words", () => {
|
|
const result = mergeCaptionSources(
|
|
[
|
|
{
|
|
id: "mic",
|
|
startMs: 0,
|
|
endMs: 3000,
|
|
text: "Hello again",
|
|
words: [
|
|
{ text: "Hello", startMs: 0, endMs: 600 },
|
|
{ text: "again", startMs: 2400, endMs: 3000 },
|
|
],
|
|
},
|
|
],
|
|
[{ id: "system", startMs: 1000, endMs: 1800, text: "In the gap" }],
|
|
);
|
|
expect(result.map((cue) => cue.text)).toContain("In the gap");
|
|
});
|
|
|
|
it("retains the unopposed portions of an untimed system cue", () => {
|
|
const result = mergeCaptionSources(
|
|
[
|
|
{
|
|
id: "mic",
|
|
startMs: 0,
|
|
endMs: 3000,
|
|
text: "Mic",
|
|
words: [{ text: "Mic", startMs: 1000, endMs: 2000 }],
|
|
},
|
|
],
|
|
[{ id: "system", startMs: 0, endMs: 3000, text: "System paragraph" }],
|
|
);
|
|
expect(
|
|
result
|
|
.filter((cue) => cue.text === "System paragraph")
|
|
.map(({ startMs, endMs }) => [startMs, endMs]),
|
|
).toEqual([
|
|
[0, 1000],
|
|
[2000, 3000],
|
|
]);
|
|
});
|