Files
app-hejyou/src/utils/chipTimings.js
admin e7b4ec571e feat: persönlichere Profilseite + iOS-App-Setup
Profil: Begrüßung in Zielsprache, Kategorie-Punkte-Übersicht,
ruhigerer Header (kein rotierender Avatar/Online-Dot), Notch-Fix
und kompaktere Aktivitäts-Heatmap. Außerdem Capacitor-iOS-Projekt
und diverse Auth/Feed/Audio-Verbesserungen aus dem Premium-Redesign.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 12:55:13 +02:00

49 lines
1.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Karaoke-Timing: aus dem Satz + ElevenLabs-`alignment` pro Wort-/Objekt-Chip
// ein Zeitfenster (Medienzeit in Sekunden) berechnen, damit beim Vorlesen das
// gerade gesprochene Chip markiert werden kann.
//
// alignment-Format (ElevenLabs /with-timestamps):
// { characters: string[],
// character_start_times_seconds: number[],
// character_end_times_seconds: number[] }
// `characters.join('')` entspricht dem vertonten Klartext (Platzhalter durch Labels ersetzt).
const CHIP_RE = /\{\{([^.]+)\.(w|o):([0-9a-f-]{36})\}\}/g
// Liefert [{ id, label, start, end }] ein Eintrag pro Chip-Vorkommen im Satz.
export function buildChipTimings(sentence, alignment) {
if (!sentence || !alignment) return []
const chars = alignment.characters
const starts = alignment.character_start_times_seconds
const ends = alignment.character_end_times_seconds
if (!Array.isArray(chars) || !Array.isArray(starts) || !Array.isArray(ends) || !chars.length) return []
const plain = chars.join('')
// Satz in derselben Reihenfolge nach Chip-Tokens absuchen; Labels sequenziell
// im Klartext lokalisieren (toleriert kleine Whitespace-Unterschiede).
const timings = []
let cursor = 0
for (const m of sentence.matchAll(CHIP_RE)) {
const label = m[1]
const id = m[3]
const startIdx = plain.indexOf(label, cursor)
if (startIdx === -1) continue
const endIdx = startIdx + label.length - 1
cursor = endIdx + 1
const start = starts[startIdx]
const end = ends[Math.min(endIdx, ends.length - 1)]
if (typeof start !== 'number' || typeof end !== 'number') continue
timings.push({ id, label, start, end })
}
return timings
}
// Id des Chips, dessen Zeitfenster t (Sekunden) enthält sonst null.
export function activeChipIdAt(timings, t) {
if (!timings?.length || typeof t !== 'number') return null
for (const c of timings) {
if (t >= c.start && t < c.end) return c.id
}
return null
}