Parsing Video Captions: Building a Secure Client-Side ASS to SRT Converter
Hey DEV community! 👋 When distributing video content globally, providing highly compatible caption files is an essential step to ensure broad audience engagement. While the ASS (Advanced SubStation Alpha) format offers rich stylization—including precise coordinate offsets, nested typography effects, and dynamic kinetic movement—it introduces major compatibility challenges. Most consumer-grade media players, web-based video controls, and streaming networks require the simpler, clean, and lightweight SRT (SubRip) format. To automate this workflow without relying on server-side conversions, I built an entirely browser-based ASS to SRT Subtitle Converter in vanilla JavaScript. In this post, we will inspect the architectural differences between these formats and look at how to implement a secure, local parsing utility. Format Specifications: ASS vs. SRT Before writing the parser, we must analyze the structural patterns of both formats. 1. ASS (Advanced SubStation Alpha) ASS is a multi-section, text-based format containing comprehensive styling dictionaries and scripting headers. The actual dialogue events are stored under the [Events] block: [Events] Format: Layer, Start, End, Style, Actor, MarginL, MarginR, MarginV, Effect, Text Dialogue: 0,0:01:12.45,0:01:15.90,Default,,0,0,0,,Welcome to our latest {\b1}video production tutorial{\b0}! 2. SRT (SubRip) SRT is a basic flat-file structure containing sequential numbering, start/end timestamps separated by the --> token, and unstyled text blocks: 1 00:01:12,450 --> 00:01:15,900 Welcome to our latest video production tutorial! Under the Hood: The Interpolation and Sanitation Pipeline To translate ASS into SRT, our client-side engine performs two main operations: 1. Timestamp Conversion ASS tracks timing in centiseconds (1/100 of a second), whereas SRT relies on milliseconds (1/1000 of a second). The converter implements this conversion: Milliseconds (ms)=Centiseconds (CS)×10 Hours, minutes, and seconds are padded with leading zeros to maintain the standard double-digit layout required by the SRT specification. 2. Tag Sanitation We use regular expressions to strip out formatting overrides (like {\b1}, {\pos(100,200)}, or font details) to ensure universal hardware compatibility: Styling Tag Removal: Removes everything within curly brackets: text.replace(/\{[^}]*\}/g, '') Line Break Normalization: Translates alignment breaks into standard Unix line breaks: text.replace(/\\N|\\n/gi, '\n') JavaScript Parser Implementation Below is the clean, self-contained JavaScript utility that splits, validates, and decodes the raw inputs: /** * Converts a raw ASS subtitle string into clean SRT format. * @param {string} assInput - The raw ASS text file content * @returns {string} The converted SRT output */ function convertAssToSrt(assInput) { if (!assInput) return ''; const lines = assInput.split('\n'); let srtContent = []; let subtitleNumber = 1; let inEventsSection = false; for (const line of lines) { const trimmedLine = line.trim(); if (trimmedLine === '[Events]') { inEventsSection = true; continue; } if (trimmedLine.startsWith('[') && trimmedLine !== '[Events]') { inEventsSection = false; } if (inEventsSection && trimmedLine.startsWith('Dialogue:')) { const parts = trimmedLine.split(','); if (parts.length < 10) continue; const assStartTime = parts[1].trim(); const assEndTime = parts[2].trim(); // Recombine the remaining elements in case dialogue contains commas const assText = parts.slice(9).join(','); const srtStartTime = formatAssTimeToSrt(assStartTime); const srtEndTime = formatAssTimeToSrt(assEndTime); const cleanText = assText.replace(/\{[^}]*\}/g, '').replace(/\\N|\\n/gi, '\n').trim(); if (cleanText) { srtContent.push(subtitleNumber.toString()); srtContent.push(`${srtStartTime} --> ${srtEndTime}`); srtContent.push(cleanText); srtContent.push(''); // Empty line separator subtitleNumber++; } } } if (srtContent.length === 0) { throw new Error("No dialogue events discovered. Ensure your subtitle contains an [Events] section."); } return srtContent.join('\n').trim(); } /** * Translates ASS time format (H:MM:SS.CS) to SRT format (HH:MM:SS,ms) */ function formatAssTimeToSrt(assTime) { const parts = assTime.split(':'); let h = parseInt(parts[0], 10); let m = parseInt(parts[1], 10); let s_cs = parseFloat(parts[2]); let s = Math.floor(s_cs); let cs = Math.round((s_cs - s) * 100); let ms = cs * 10; h = h.toString().padStart(2, '0'); m = m.toString().padStart(2, '0'); s = s.toString().padStart(2, '0'); ms = ms.toString().padStart(3, '0'); return `${h}:${m}:${s},${ms}`; } Client-Side Security and Privacy Many free online converters process your subtitle files on remote servers. When translating unreleased films, private lectures, or company product videos, this network exposure introduces unnecessary data risks. By executing the regex cleaning and timing interpolation entirely on the client side: No Database Logging: Your raw text strings remain strictly within your local browser memory and are flushed instantly when the page is reset. Universal Offline Support: Once the tool page is loaded, the converter operates without any internet connectivity. Try the Live Utility I have integrated this client-side transcoder directly into my online toolbox. If you need a private, rapid way to flatten your caption files and remove styling tags for universal hardware compatibility, feel free to use it: 👉 Live Link: Online ASS to SRT Subtitle Converter - Vo Viet Hoang Let's Connect! How do you handle subtitle standardization and localized video distributions in your development pipeline? Do you write command-line scripts or use browser utilities? Let let me know in the comments section below! Happy coding! 🚀
This is a summary aggregated from Dev.to. Read the complete article on the original site:
Read full article at Dev.to