From fd96ad00664ff6d60369eb9865a875c81dfa0bf4 Mon Sep 17 00:00:00 2001 From: PystoyPlayer Date: Tue, 14 Apr 2026 04:50:57 +0300 Subject: [PATCH] fix russian subs style --- crunchy.ts | 72 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 54 insertions(+), 18 deletions(-) diff --git a/crunchy.ts b/crunchy.ts index df35438..3c175cd 100644 --- a/crunchy.ts +++ b/crunchy.ts @@ -43,19 +43,54 @@ import { CrunchyVideoPlayStreams, CrunchyAudioPlayStreams } from './@types/enums import { randomUUID } from 'node:crypto'; import { FetchParams } from './modules/module.fetch'; -export type sxItem = { - language: langsData.LanguageItem; - path: string; - file: string; - title: string; - fonts: Font[]; -}; - -export default class Crunchy implements ServiceClass { - public cfg: yamlCfg.ConfigObject; - public locale: string; - private token: Record; - private req: reqModule.Req; +export type sxItem = { + language: langsData.LanguageItem; + path: string; + file: string; + title: string; + fonts: Font[]; +}; + +const defaultRussianAssStyle = 'Style: Default,Tahoma,22,&H00FFFFFF,&H000000FF,&H00000000,&H96000000,0,0,0,0,100,100,0,0,1,2,1,2,0010,0010,0025,204'; + +function normalizeRussianBrokenAssDialogue(text: string) { + return text + .replace(/[ \t]*\\N[ \t]*/g, '\\N') + .split('\\N') + .map((line) => { + const speakerLineMatch = line.match(/^\s*((?:\{[^}]*\}\s*)*)-\s*(.*)$/u); + if (!speakerLineMatch) return line; + + const prefix = speakerLineMatch[1].replace(/\s+$/u, ''); + const content = speakerLineMatch[2].replace(/(\S)\s*—\s*(?=\S)/gu, '$1 – '); + return `${prefix}— ${content}`; + }) + .join('\\N'); +} + +function normalizeRussianBrokenAss(ass: string) { + const hasLegacyCenterStyle = /^Style:\s*style\.center\s*,\s*Arial\s*,/im.test(ass); + if (!hasLegacyCenterStyle) return ass; + + const hasDefaultStyle = /^Style:\s*Default\s*,/im.test(ass); + + ass = hasDefaultStyle + ? ass.replace(/^Style:\s*style\.center\s*,\s*Arial\s*,.*\r?\n?/gim, '') + : ass.replace(/^Style:\s*style\.center\s*,\s*Arial\s*,.*$/gim, defaultRussianAssStyle); + + ass = ass.replace(/^((?:Dialogue|Comment):\s*[^,\n]*,[^,\n]*,[^,\n]*),\s*style\.center(\s*,.*)$/gim, '$1,Default$2'); + ass = ass.replace(/^(Dialogue:\s*(?:[^,\n]*,){9})(.*)$/gm, (_line, prefix, text) => { + return `${prefix}${normalizeRussianBrokenAssDialogue(text)}`; + }); + + return ass; +} + +export default class Crunchy implements ServiceClass { + public cfg: yamlCfg.ConfigObject; + public locale: string; + private token: Record; + private req: reqModule.Req; private cmsToken: { cms?: Record; cms_beta?: Record; @@ -2999,11 +3034,12 @@ export default class Crunchy implements ServiceClass { sBody = newLines.join('\n'); } - // Force outline thickness for ru-RU: if the 17th field (Outline) equals 2.6 → 2 - if (langItem.cr_locale === 'ru-RU') { - sBody = sBody.replace(/^[ \t]*(Style:\s*[^,\n]*(?:,[^,\n]*){15}),\s*2(?:[.,]6(?:0+)?)?(\s*,)/gm, '$1,2$2'); - } - } + // Force outline thickness for ru-RU: if the 17th field (Outline) equals 2.6 → 2 + if (langItem.cr_locale === 'ru-RU') { + sBody = normalizeRussianBrokenAss(sBody); + sBody = sBody.replace(/^[ \t]*(Style:\s*[^,\n]*(?:,[^,\n]*){15}),\s*2(?:[.,]6(?:0+)?)?(\s*,)/gm, '$1,2$2'); + } + } sxData.title = langItem.language; sxData.fonts = fontsData.assFonts(sBody) as Font[];