mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-09-08 17:16:18 -07:00
fix(subtitles): improve secondary subtitle extraction and display (#215)
This commit is contained in:
@@ -1928,10 +1928,6 @@ body.layer-modal #overlay {
|
||||
text-align: center;
|
||||
font-size: 24px;
|
||||
line-height: 1.5;
|
||||
/* Backstop: pathological tracks (karaoke typesetting, sign spam) must never grow
|
||||
the hover-pause band beyond a top strip. ~4 lines at line-height 1.5. */
|
||||
max-height: 6em;
|
||||
overflow: hidden;
|
||||
color: #ffffff;
|
||||
-webkit-text-stroke: 0.45px rgba(0, 0, 0, 0.7);
|
||||
paint-order: stroke fill;
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -667,12 +667,17 @@ function isKaraokeLikeLineSet(lines: string[]): boolean {
|
||||
}
|
||||
|
||||
function collapseFullLineFallbackCopies(lines: string[]): string[] {
|
||||
const seen = new Set<string>();
|
||||
const seenExact = new Set<string>();
|
||||
const seenFlattened = new Set<string>();
|
||||
return lines.filter((line) => {
|
||||
const identity = flattenedSecondarySubtitleLineIdentity(line);
|
||||
if (!identity) return true;
|
||||
if (seen.has(identity)) return false;
|
||||
seen.add(identity);
|
||||
const exactIdentity = line.normalize('NFKC');
|
||||
if (seenExact.has(exactIdentity)) return false;
|
||||
seenExact.add(exactIdentity);
|
||||
|
||||
const flattenedIdentity = flattenedSecondarySubtitleLineIdentity(line);
|
||||
if (!flattenedIdentity) return true;
|
||||
if (seenFlattened.has(flattenedIdentity)) return false;
|
||||
seenFlattened.add(flattenedIdentity);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user