Under the Hood: The Architecture of Bulk SRT/VTT Subtitle Time-Shifter
Cloud tools that “fix out of sync subtitles” often want the video plus the SRT. That is an unreleased-title leak. Shift subtitle timing is a text problem: SRT and WebVTT store cues as timestamps plus payload. An SRT parser online only needs to rewrite clocks. This page accepts one or many .srt/.vtt files, applies a millisecond offset (positive delays cues; negative advances them), and downloads new files. Cue dialogue is not sent to a translation API; it is not even tokenized beyond the timestamp regex.
Batch is a loop over File[], not a zip upload. Each file is decoded with TextDecoder from an ArrayBuffer. Detection of VTT is the .vtt extension or a WEBVTT signature. Output MIME is text/vtt or application/x-subrip. The original files on disk are never overwritten.
Memory Allocation & Browser Threading Mechanics
Subtitle files are small relative to video: hundreds of kilobytes typically. Still, batch mode processes files sequentially so you do not hold ten decoded strings plus ten rewritten strings at once. String.prototype.replace with a global timestamp regex allocates one output string per file. The main thread is appropriate; a Worker would cost more in structured-clone of the text than it would save. UI lock risk is the browser’s multiple download prompts, not CPU.
Algorithmic Deep-Dive: Code Snippet & Logical Analysis
const SRT_TIME = /(\d{2}:\d{2}:\d{2}[,.]\d{3})/g;
function parseStamp(s) {
const [h, m, rest] = s.replace(",", ".").split(":");
const [sec, ms] = rest.split(".");
return (Number(h) * 3600 + Number(m) * 60 + Number(sec)) * 1000 + Number(ms);
}
function formatStamp(ms, vtt) {
const t = Math.max(0, ms);
const pad = (n, w = 2) => String(n).padStart(w, "0");
const h = Math.floor(t / 3600000);
const m = Math.floor((t % 3600000) / 60000);
const s = Math.floor((t % 60000) / 1000);
return pad(h) + ":" + pad(m) + ":" + pad(s) + (vtt ? "." : ",") + pad(t % 1000, 3);
}
function shiftSubtitles(text, offsetMs, vtt) {
return text.replace(SRT_TIME, (stamp) =>
formatStamp(parseStamp(stamp) + offsetMs, vtt || stamp.includes(".")));
}Arrow cues (-->) are left intact because the regex only captures the clock tokens. Comma vs dot is preserved per token (SRT commas, VTT dots). Negative offsets clamp at zero milliseconds so players do not receive negative timestamps. Hours can grow beyond 99 in theory; two-digit padding still works for typical feature-length offsets of a few seconds.
The Local Storage Sandbox & Volatile Data Pipelines
Each result is a Blob passed to downloadText, which uses URL.createObjectURL and revokes immediately after click. Filenames get a .shifted infix so you do not clobber the source in your downloads folder. No IndexedDB of “projects.” RAM holds the current file’s strings only for the duration of the loop iteration after the previous Blob is queued.
Verifying True Privacy: How to Audit Your Browser's Network Tab
F12 → Network → clear. Drop an SRT, set offset to 2 seconds, download. Search request bodies for a distinctive cue line. It must not appear. There is no caption CDN and no multipart file field. That is how you verify bulk SRT/VTT shifting is local.