feat: time offset playback

This commit is contained in:
2026-02-26 14:47:43 +01:00
parent 4a84cd5789
commit 98efb55611
8 changed files with 59 additions and 12 deletions
+17 -9
View File
@@ -94,6 +94,8 @@ export function groupAlignedWordsToLineItems(
const original = String(w.word || '')
const textNoNewline = original.replace(/\n/g, ' ')
const textNoTag = removeTags ? textNoNewline.replace(/^\s*\[[^\]]+\]\s*/i, '') : textNoNewline
const next = alignedWords[i + 1] || null
const gapToNext = next ? Math.max(0, Number(next.startS || 0) - Number(w.endS || 0)) : 0
if (buf.length === 0) start = Number(w.startS || 0)
if (isSectionTag(textNoNewline)) {
@@ -132,10 +134,11 @@ export function groupAlignedWordsToLineItems(
buf.push(wordItem)
const eolByNewline = /\n/.test(original)
const eolByPause = gapToNext >= 0.6 // threshold for a logical break
const eolByPunct = isSentenceEnd(textNoTag)
const isLast = i === alignedWords.length - 1
if (buf.length && (eolByNewline || eolByPunct || isLast)) {
if (buf.length && (eolByNewline || eolByPause || eolByPunct || isLast)) {
pushLine(buf, Number(w.endS || 0))
buf = []
start = null
@@ -151,6 +154,7 @@ const alphaWhite = (alpha) => `rgba(255,255,255,${alpha})`
export default function KaraokeLyrics({
alignedWords = [],
currentTimeS = 0,
timeOffsetS = 0,
showContext = true,
removeTags = true,
mode = 'focus',
@@ -170,17 +174,21 @@ export default function KaraokeLyrics({
)
const activeLines = mode === 'teleprompter' ? teleprompterData : lines
const effectiveTimeS = useMemo(
() => Math.max(0, Number(currentTimeS || 0) + Number(timeOffsetS || 0)),
[currentTimeS, timeOffsetS]
)
const currentLineIdx = useMemo(() => {
if (!activeLines || activeLines.length === 0) return -1
if (effectiveTimeS <= (activeLines[0]?.startS || 0)) return 0
for (let i = 0; i < activeLines.length; i++) {
const L = activeLines[i]
if (currentTimeS >= (L.startS || 0) && currentTimeS <= (L.endS || 0)) return i
if (effectiveTimeS < (L.startS || 0)) return Math.max(0, i - 1)
if (effectiveTimeS >= (L.startS || 0) && effectiveTimeS <= (L.endS || 0)) return i
}
if (currentTimeS > (activeLines[activeLines.length - 1]?.endS || 0))
return activeLines.length - 1
return -1
}, [activeLines, currentTimeS])
return activeLines.length - 1
}, [activeLines, effectiveTimeS])
if (!activeLines.length) return null
@@ -208,8 +216,8 @@ export default function KaraokeLyrics({
}}
>
{activeLines.map((line, idx) => {
const isUpcoming = currentTimeS < (line.startS || 0)
const isPast = currentTimeS > (line.endS || 0)
const isUpcoming = effectiveTimeS < (line.startS || 0)
const isPast = effectiveTimeS > (line.endS || 0)
const lineKey = `${line.startS}-${idx}`
if (isUpcoming) {
@@ -258,7 +266,7 @@ export default function KaraokeLyrics({
>
{line.words.map((word, wordIndex) => {
const duration = Math.max(0.001, (word.endS || 0) - (word.startS || 0))
const progress = clamp01((currentTimeS - (word.startS || 0)) / duration)
const progress = clamp01((effectiveTimeS - (word.startS || 0)) / duration)
const alpha = inactiveAlpha + (activeAlpha - inactiveAlpha) * progress
const text = word.text
return (