mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-10-30 09:16:55 +00:00
fix(cli): preserve subtitle cues across line endings (#5223)
* fix(cli): preserve subtitle cues across line endings * refactor(cli): preserve newline handling while reconciling subtitle tests * refactor(cli): normalize WebVTT line endings at the parser * style(cli): format the retained subtitle test block
This commit is contained in:
@@ -280,6 +280,47 @@ Short format
|
||||
});
|
||||
});
|
||||
|
||||
describe("subtitle line endings", () => {
|
||||
it.each([
|
||||
["srt", "\r\n"],
|
||||
["srt", "\r"],
|
||||
["vtt", "\r\n"],
|
||||
["vtt", "\r"],
|
||||
])("keeps separate %s cues with %j line endings", (ext, newline) => {
|
||||
const timestamp = ext === "srt" ? "," : ".";
|
||||
const lines = [
|
||||
...(ext === "vtt" ? ["WEBVTT", ""] : []),
|
||||
"1",
|
||||
`00:00:01${timestamp}000 --> 00:00:03${timestamp}500`,
|
||||
"<b>Hello</b> world",
|
||||
"Again",
|
||||
"",
|
||||
"2",
|
||||
`00:00:04${timestamp}000 --> 00:00:06${timestamp}000`,
|
||||
"How are you",
|
||||
"",
|
||||
];
|
||||
const { words, format } = loadTranscript(tmpFile(`captions.${ext}`, lines.join(newline)));
|
||||
expect(format).toBe(ext);
|
||||
expect(words).toEqual([
|
||||
{ text: "Hello world Again", start: 1, end: 3.5, id: "w0" },
|
||||
{ text: "How are you", start: 4, end: 6, id: "w1" },
|
||||
]);
|
||||
expect(formatSrt(words, { preGrouped: true })).toBe(
|
||||
"1\n00:00:01,000 --> 00:00:03,500\nHello world Again\n\n2\n00:00:04,000 --> 00:00:06,000\nHow are you\n",
|
||||
);
|
||||
});
|
||||
|
||||
it("preserves WebVTT cues when newline forms are mixed", () => {
|
||||
const source =
|
||||
"WEBVTT\r\n\r\n00:01.000 --> 00:02.000\rFirst phrase\r\r00:03.000 --> 00:04.000\nSecond phrase\n";
|
||||
expect(loadTranscript(tmpFile("mixed.vtt", source)).words).toEqual([
|
||||
{ text: "First phrase", start: 1, end: 2, id: "w0" },
|
||||
{ text: "Second phrase", start: 3, end: 4, id: "w1" },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("caption formatting", () => {
|
||||
it.each([
|
||||
["R&D <config> next", "R&D <config> next"],
|
||||
|
||||
@@ -190,7 +190,7 @@ function parseOpenAI(data: Record<string, unknown>): Word[] {
|
||||
function parseSrt(content: string): Word[] {
|
||||
// SRT doesn't have word-level timestamps — parse as phrase-level entries.
|
||||
// Each cue becomes one "word" entry (the full phrase).
|
||||
const blocks = content.trim().split(/\n\n+/);
|
||||
const blocks = content.replace(/\r\n?/g, "\n").trim().split(/\n\n+/);
|
||||
const words: Word[] = [];
|
||||
|
||||
for (const block of blocks) {
|
||||
@@ -227,7 +227,7 @@ function decodeVttText(text: string): string {
|
||||
}
|
||||
|
||||
function parseVtt(content: string): Word[] {
|
||||
const body = content.replace(/^WEBVTT[^\n]*\n/, "");
|
||||
const body = content.replace(/\r\n?/g, "\n").replace(/^WEBVTT[^\n]*\n/, "");
|
||||
// VTT is structurally similar to SRT (without numeric indices)
|
||||
const blocks = body.trim().split(/\n\n+/);
|
||||
const words: Word[] = [];
|
||||
|
||||
Reference in New Issue
Block a user