import assert from 'node:assert/strict'; import test from 'node:test'; import { convertYoutubeTimedTextToVtt, normalizeYoutubeAutoVtt } from './timedtext'; test('convertYoutubeTimedTextToVtt leaves malformed numeric entities literal', () => { const result = convertYoutubeTimedTextToVtt( '

� � A

', ); assert.equal( result, ['WEBVTT', '', '00:00:00.000 --> 00:00:01.000', '� � A', ''].join('\n'), ); }); test('convertYoutubeTimedTextToVtt does not swallow text after zero-length overlap rows', () => { const result = convertYoutubeTimedTextToVtt( [ '', '

今日は

', '

今日はいい天気ですね

', '

今日はいい天気ですね

', '
', ].join(''), ); assert.equal( result, [ 'WEBVTT', '', '00:00:00.000 --> 00:00:00.999', '今日は', '', '00:00:01.000 --> 00:00:03.000', 'いい天気ですね', '', ].join('\n'), ); }); test('convertYoutubeTimedTextToVtt extends rolling captions to the next window event', () => { // Real-world shape of YouTube's sentence-level auto captions: window-append // filler rows (a="1", sometimes without d) mark the display timeline, while // long text rows carry a placeholder d="3000" far shorter than the speech. const result = convertYoutubeTimedTextToVtt( [ '', '

\n

', '

ありがとうって言えないよね。こんなんじゃ。

', '

\n

', '

私だったら無理だよ。

', '
', ].join('\n'), ); assert.equal( result, [ 'WEBVTT', '', '00:01:38.560 --> 00:01:46.950', 'ありがとうって言えないよね。こんなんじゃ。', '', '00:01:46.960 --> 00:01:50.759', '私だったら無理だよ。', '', ].join('\n'), ); }); test('normalizeYoutubeAutoVtt strips cumulative rolling-caption prefixes', () => { const result = normalizeYoutubeAutoVtt( [ 'WEBVTT', '', '00:00:01.000 --> 00:00:02.000', '今日は', '', '00:00:02.000 --> 00:00:03.000', '今日はいい天気ですね', '', '00:00:03.000 --> 00:00:04.000', '今日はいい天気ですね本当に', '', ].join('\n'), ); assert.equal( result, [ 'WEBVTT', '', '00:00:01.000 --> 00:00:02.000', '今日は', '', '00:00:02.000 --> 00:00:03.000', 'いい天気ですね', '', '00:00:03.000 --> 00:00:04.000', '本当に', '', ].join('\n'), ); });