Something went wrong. Try again.
Monorepo for Aesthetic.Computer aesthetic.computer
Something went wrong. Try again.
JavaScript
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465// Shared caption segmentation for burned pixels, embedded subtitles, and VTT.// One timeline must drive every representation; two slightly different split// rules can otherwise put a soft cue over the next burned phrase.
const HAS_WORD = /[\p{L}\p{N}]/u;
// Karaoke highlights need a visible letter or number. Languages such as French// can emit punctuation as its own timed token (for example the space before// ":"); keep that token in the base phrase without rasterizing an empty-looking// highlight layer for it.export function isHighlightableCaptionToken(text) { return HAS_WORD.test(text);}
export function captionPhrases(beats, { maxWords = 6, pauseMs = 380 } = {}) { const out = []; for (const beat of beats) { let current = []; const flush = () => { if (!current.length) return; const words = current.map((word) => ({ text: word.text, from: beat.offsetSec + word.fromMs / 1000, to: beat.offsetSec + word.toMs / 1000, })); const text = words.map((word) => word.text).join(" "); if (!HAS_WORD.test(text)) { const previous = out[out.length - 1]; if (previous) { previous.text += ` ${text}`; previous.to = words.at(-1).to; previous.words.push(...words); } current = []; return; } out.push({ from:words[0].from, to:words.at(-1).to, text, words, color:beat.captionColor || null, }); current = []; };
for (const [index, word] of beat.words.entries()) { current.push(word); const next = beat.words[index + 1]; const punctuation = /[.!?,—]$/.test(word.text); const pause = next && next.fromMs - word.toMs >= pauseMs; if (punctuation || pause || current.length >= maxWords) flush(); } flush(); }
// Clamp any rounding-level collision defensively. Two phrases must never // paint at once, even if a future voice provider returns overlapping words. for (let index = 0; index + 1 < out.length; index += 1) { if (out[index].to > out[index + 1].from) { const boundary = (out[index].to + out[index + 1].from) / 2; out[index].to = boundary; out[index + 1].from = boundary; } } return out;}