UNPKG

subforge

Version:

High-performance subtitle toolkit for parsing, converting, and authoring across 20+ formats.

118 lines (102 loc) 3.38 kB
import type { SubtitleDocument } from '../../../core/types.ts' /** * Options for serializing to QuickTime Text format */ export interface QTSerializeOptions { /** Font family name (default: 'Helvetica') */ font?: string /** Font size in points (default: 12) */ size?: number /** Text color as RGB values 0-255 (default: white [255, 255, 255]) */ textColor?: [number, number, number] /** Background color as RGB values 0-255 (default: black [0, 0, 0]) */ backColor?: [number, number, number] /** Text justification (default: 'center') */ justify?: 'left' | 'center' | 'right' /** Time scale units per second (default: 1000) */ timeScale?: number /** Video width in pixels (default: 320) */ width?: number /** Video height in pixels (default: 60) */ height?: number } /** * Serializes a subtitle document to QuickTime Text format * * @param doc - Subtitle document to serialize * @param opts - Serialization options for formatting and display * @returns QuickTime Text formatted string * * @example * ```ts * const qt = toQT(doc, { * font: 'Arial', * size: 14, * textColor: [255, 255, 255], * justify: 'center' * }) * ``` */ export function toQT(doc: SubtitleDocument, opts: QTSerializeOptions = {}): string { const font = opts.font ?? 'Helvetica' const size = opts.size ?? 12 const textColor = opts.textColor ?? [255, 255, 255] const backColor = opts.backColor ?? [0, 0, 0] const justify = opts.justify ?? 'center' const timeScale = opts.timeScale ?? 1000 const width = opts.width ?? 320 const height = opts.height ?? 60 // Convert RGB (0-255) to QuickTime color (0-65535) const formatColor = (rgb: [number, number, number]): string => { const r = Math.floor(rgb[0] * 257) const g = Math.floor(rgb[1] * 257) const b = Math.floor(rgb[2] * 257) return `${r}, ${g}, ${b}` } let result = '' // Header result += '{QTtext} ' result += `{font:${font}}\n` result += '{plain} ' result += `{size:${size}} ` result += `{textColor: ${formatColor(textColor)}}\n` result += `{backColor: ${formatColor(backColor)}}\n` result += `{justify:${justify}}\n` result += `{timeScale:${timeScale}}\n` result += `{width:${width}} ` result += `{height:${height}}\n` result += '{timeStamps:absolute}\n\n' // Events const events = doc.events const len = events.length for (let i = 0; i < len; i++) { const event = events[i] if (!event) continue const text = event.text ?? '' // Format timestamp const timestamp = formatTime(event.start) result += `[${timestamp}]\n` result += text + '\n\n' } // Add final timestamp to clear subtitle if (len > 0) { const lastEvent = events[len - 1] if (lastEvent) { const endTimestamp = formatTime(lastEvent.end) result += `[${endTimestamp}]\n` } } return result } function formatTime(ms: number): string { const totalSeconds = Math.floor(ms / 1000) const milliseconds = ms % 1000 const hours = Math.floor(totalSeconds / 3600) const minutes = Math.floor((totalSeconds % 3600) / 60) const seconds = totalSeconds % 60 const hh = hours.toString().padStart(2, '0') const mm = minutes.toString().padStart(2, '0') const ss = seconds.toString().padStart(2, '0') const mmm = milliseconds.toString().padStart(3, '0') return `${hh}:${mm}:${ss}.${mmm}` }