fix(subtitles): improve secondary subtitle extraction and display (#215)

This commit is contained in:
2026-08-23 01:56:49 -07:00
committed by GitHub
parent 0a0aa3ec98
commit 509dc5bf7f
20 changed files with 503 additions and 37 deletions
+19 -9
View File
@@ -1424,11 +1424,8 @@ test('subtitle annotation CSS underlines JLPT tokens without changing token colo
);
});
test('prepareSecondarySubtitleLines preserves short stacks without layer metadata', () => {
test('prepareSecondarySubtitleLines collapses exact short copies in stacks', () => {
assert.deepEqual(prepareSecondarySubtitleLines('Your\\NYour\\NYour\\NYour\\Nmosaic'), [
'Your',
'Your',
'Your',
'Your',
'mosaic',
]);
@@ -1438,6 +1435,15 @@ test('prepareSecondarySubtitleLines preserves short stacks without layer metadat
]);
});
test('prepareSecondarySubtitleLines collapses exact short sign copies beside dialogue', () => {
const liveText = "And for today's sports festival...\nEntrance\nEntrance";
assert.deepEqual(prepareSecondarySubtitleLines(liveText), [
"And for today's sports festival...",
'Entrance',
]);
});
test('prepareSecondarySubtitleLines collapses karaoke syllable spam into one deduped line', () => {
// Karaoke-typeset OP/ED: one ASS event per syllable, duplicated across layers,
// joined with \N by mpv's secondary-sub-text.
@@ -1448,10 +1454,10 @@ test('prepareSecondarySubtitleLines collapses karaoke syllable spam into one ded
assert.deepEqual(prepareSecondarySubtitleLines(karaoke), ['ya This no ma ups']);
});
test('prepareSecondarySubtitleLines preserves repeated short dialogue without layer metadata', () => {
test('prepareSecondarySubtitleLines collapses exact repeated short lines', () => {
const dialogue = ['Wait', 'Wait', 'Wait'];
assert.deepEqual(prepareSecondarySubtitleLines(dialogue.join('\\N')), dialogue);
assert.deepEqual(prepareSecondarySubtitleLines(dialogue.join('\\N')), ['Wait']);
});
test('prepareSecondarySubtitleLines collapses punctuation variants of a full-sentence fallback', () => {
@@ -1469,6 +1475,10 @@ test('prepareSecondarySubtitleLines preserves short simultaneous dialogue withou
assert.deepEqual(prepareSecondarySubtitleLines(dialogue.join('\\N')), dialogue);
});
test('prepareSecondarySubtitleLines preserves distinct short lines with internal whitespace', () => {
assert.deepEqual(prepareSecondarySubtitleLines('AB\\NA B'), ['AB', 'A B']);
});
test('prepareSecondarySubtitleLines keeps normal dialogue lines intact', () => {
const dialogue = ' I never expected this. \\N\\N But here we are. ';
@@ -1490,13 +1500,13 @@ test('prepareSecondarySubtitleLines strips ASS override tags and handles empty i
assert.deepEqual(prepareSecondarySubtitleLines('{\\an8}'), []);
});
test('secondary subtitle root CSS caps height so hover-pause band stays a top strip', () => {
test('secondary subtitle root CSS does not clip long subtitle stacks', () => {
const srcCssPath = path.join(process.cwd(), 'src', 'renderer', 'style.css');
const cssText = fs.readFileSync(srcCssPath, 'utf-8');
const secondaryRootBlock = extractClassBlock(cssText, '#secondarySubRoot');
assert.match(secondaryRootBlock, /max-height:\s*6em;/);
assert.match(secondaryRootBlock, /overflow:\s*hidden;/);
assert.doesNotMatch(secondaryRootBlock, /max-height\s*:/);
assert.doesNotMatch(secondaryRootBlock, /overflow\s*:\s*hidden/);
});
test('applySubtitleStyle sets known-word maturity color variables', () => {