diff --git a/libs/ui/playback/src/lib/web-video-support/external-subtitle-cues.util.spec.ts b/libs/ui/playback/src/lib/web-video-support/external-subtitle-cues.util.spec.ts index ca3b9a65a..7f2505a39 100644 --- a/libs/ui/playback/src/lib/web-video-support/external-subtitle-cues.util.spec.ts +++ b/libs/ui/playback/src/lib/web-video-support/external-subtitle-cues.util.spec.ts @@ -76,6 +76,21 @@ describe('external-subtitle-cues.util', () => { expect(decoded).toContain('À table'); }); + it('keeps a minimal isolated-accent CP1252 caption Latin ("À la")', () => { + // One lone Cyrillic-looking letter and two ASCII letters passed + // the earlier share guard; single-letter words must carry no + // script evidence at all. + const cp1252 = [ + 0xc0, // À + ...Array.from( + ' la\n1\n00:00:01,000 --> 00:00:02,000\n' + ).map((c) => c.charCodeAt(0)), + ]; + expect(decodeExternalSubtitleBytes(toBuffer(cp1252))).toContain( + 'À la' + ); + }); + it('keeps an isolated accented CP1252 word Latin ("À table")', () => { // À=0xC0 decodes under CP1251 to the pure-Cyrillic one-letter // word "А"; without the letter-share guard that single vote diff --git a/libs/ui/playback/src/lib/web-video-support/external-subtitle-cues.util.ts b/libs/ui/playback/src/lib/web-video-support/external-subtitle-cues.util.ts index 6ed433369..137710fd3 100644 --- a/libs/ui/playback/src/lib/web-video-support/external-subtitle-cues.util.ts +++ b/libs/ui/playback/src/lib/web-video-support/external-subtitle-cues.util.ts @@ -93,15 +93,20 @@ function chooseLegacySingleByteDecode(buffer: ArrayBuffer): string { asciiLetters += word.length; } else if (/[A-Za-z]/.test(word)) { mixedLetters += word.length; - } else { + } else if (word.length >= 2) { + // A single letter carries no trustworthy script information: an + // isolated CP1252 accent ("\u00c0"/"\u00c9") decodes to a lone Cyrillic + // letter and must not count as Cyrillic evidence, while genuine + // Cyrillic dialogue always contains multi-letter words. cyrillicLetters += word.length; } } - // Two guards: mixed-script words are strong evidence of misread Latin - // text, and isolated accented CP1252 words ("\u00c0 table" \u2192 "\u0410 table") - // masquerade as tiny pure-Cyrillic words \u2014 so Cyrillic must also carry a - // meaningful share of all letters before 1251 wins. Genuinely Cyrillic + // Two guards on top of that: mixed-script words are strong evidence of + // misread Latin text ("\u00e9tait" \u2192 "\u0439tait"), and Cyrillic must carry a + // meaningful share of all letters before 1251 wins \u2014 genuinely Cyrillic // dialogue dominates its own letter count even with embedded Latin names. + // A file whose only high bytes are one or two isolated letters is + // fundamentally ambiguous; defaulting it to 1252 is the best effort. const plausiblyCyrillic = cyrillicLetters > mixedLetters * 2 && cyrillicLetters * 4 > asciiLetters;