From 908e83a2f82b63d74cbdeab90e0d0ced39f7d25c Mon Sep 17 00:00:00 2001 From: jeffvli Date: Fri, 10 Jul 2026 12:48:34 -0700 Subject: [PATCH] fix missing spaces between ttml word cues (#2214) --- .../subsonic/subsonic-structured-lyrics.ts | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/renderer/api/subsonic/subsonic-structured-lyrics.ts b/src/renderer/api/subsonic/subsonic-structured-lyrics.ts index b995a05e4..c25ab921d 100644 --- a/src/renderer/api/subsonic/subsonic-structured-lyrics.ts +++ b/src/renderer/api/subsonic/subsonic-structured-lyrics.ts @@ -17,6 +17,18 @@ type ApiStructuredLyric = NonNullable< ? T : never; +const getCueTrailingGap = ( + lineValue: string, + byteEnd: number, + nextByteStart: number | undefined, +): string => { + if (nextByteStart === undefined || nextByteStart <= byteEnd + 1) { + return ''; + } + + return sliceUtf8Bytes(lineValue, byteEnd + 1, nextByteStart - 1); +}; + const mapCueWords = ( apiCueLine: NonNullable[number], ): SyncedWordCue[] => { @@ -24,11 +36,17 @@ const mapCueWords = ( return []; } - return apiCueLine.cue.map((cue) => ({ - endMs: cue.end, - startMs: cue.start, - text: cue.value || sliceUtf8Bytes(apiCueLine.value, cue.byteStart, cue.byteEnd), - })); + return apiCueLine.cue.map((cue, cueIndex) => { + const nextCue = apiCueLine.cue?.[cueIndex + 1]; + const wordText = cue.value || sliceUtf8Bytes(apiCueLine.value, cue.byteStart, cue.byteEnd); + const trailingGap = getCueTrailingGap(apiCueLine.value, cue.byteEnd, nextCue?.byteStart); + + return { + endMs: cue.end, + startMs: cue.start, + text: wordText + trailingGap, + }; + }); }; const mapCueLine = (