{"id":50901690,"url":"https://github.com/didrod205/captionkit","last_synced_at":"2026-06-16T03:04:41.718Z","repository":{"id":361981158,"uuid":"1254461194","full_name":"didrod205/captionkit","owner":"didrod205","description":"Convert \u0026 re-sync subtitles locally — SRT ⇄ WebVTT, shift, fix drift \u0026 overlaps. Exact timecode math, zero deps. Web app + library.","archived":false,"fork":false,"pushed_at":"2026-06-02T03:33:29.000Z","size":43,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-02T05:16:03.589Z","etag":null,"topics":["captions","srt","srt-to-vtt","subtitle-converter","subtitle-sync","subtitles","video","vtt","webvtt","zero-dependency"],"latest_commit_sha":null,"homepage":"https://didrod205.github.io/captionkit/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/didrod205.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"custom":["https://elab-studio.lemonsqueezy.com/checkout/buy/5d059b89-51d0-456b-b33a-ed56994f7010"]}},"created_at":"2026-05-30T15:41:10.000Z","updated_at":"2026-06-02T03:33:31.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/didrod205/captionkit","commit_stats":null,"previous_names":["didrod205/captionkit"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/didrod205/captionkit","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/didrod205%2Fcaptionkit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/didrod205%2Fcaptionkit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/didrod205%2Fcaptionkit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/didrod205%2Fcaptionkit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/didrod205","download_url":"https://codeload.github.com/didrod205/captionkit/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/didrod205%2Fcaptionkit/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34388682,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-16T02:00:06.860Z","response_time":126,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["captions","srt","srt-to-vtt","subtitle-converter","subtitle-sync","subtitles","video","vtt","webvtt","zero-dependency"],"created_at":"2026-06-16T03:04:41.647Z","updated_at":"2026-06-16T03:04:41.710Z","avatar_url":"https://github.com/didrod205.png","language":"TypeScript","funding_links":["https://elab-studio.lemonsqueezy.com/checkout/buy/5d059b89-51d0-456b-b33a-ed56994f7010"],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n# 🎬 captionkit\n\n### Convert and re-sync subtitles — SRT ⇄ WebVTT, shift, fix drift — locally.\n\n[![npm version](https://img.shields.io/npm/v/captionkit.svg?color=success)](https://www.npmjs.com/package/captionkit)\n[![bundle size](https://img.shields.io/bundlephobia/minzip/captionkit?label=gzip)](https://bundlephobia.com/package/captionkit)\n[![CI](https://github.com/didrod205/captionkit/actions/workflows/ci.yml/badge.svg)](https://github.com/didrod205/captionkit/actions/workflows/ci.yml)\n[![types](https://img.shields.io/npm/types/captionkit.svg)](https://www.npmjs.com/package/captionkit)\n[![license](https://img.shields.io/npm/l/captionkit.svg)](./LICENSE)\n\n**[🌐 Try the free web app →](https://didrod205.github.io/captionkit/)** \u0026nbsp;·\u0026nbsp; drop a `.srt` or `.vtt`, fix the sync, download. Nothing uploaded.\n\n\u003c/div\u003e\n\n---\n\nYou exported captions and the player won't take them (it wants WebVTT, you have\nSRT). Or the subtitles play **2 seconds late**. Or they slowly **drift out of\nsync** by the end of the video. Fixing this by hand means editing dozens of\n`00:01:23,456` timestamps without breaking the format — and one stray comma or\nmissing millisecond makes the whole file invalid.\n\n**captionkit** does it correctly: convert **SRT ⇄ WebVTT**, **shift** every cue,\n**scale**/**resync** to fix drift, and **fix overlaps** — with exact\nmillisecond timecode math, **zero dependencies**, and **100% locally**.\n\n\u003e 📸 _Screenshot / demo GIF:_ `./web/screenshot.png` — record the [live app](https://didrod205.github.io/captionkit/) dropping an out-of-sync SRT and nudging it into place.\n\n## Why it exists\n\n- **AI can't reliably do this.** Re-timing every cue and emitting spec-valid\n  SRT/WebVTT (comma vs dot, `WEBVTT` header, numbering rules, CRLF) is exact,\n  fiddly format work — a chatbot will quietly corrupt a timestamp. It's a job for\n  a small, tested, deterministic tool.\n- **Privacy \u0026 friction.** Online subtitle converters make you upload your file\n  and wait. captionkit runs on your machine, instantly.\n- **The drift fix is the \"aha\".** `resync` linearly maps the first and last cue\n  to where they *should* be — repairing subtitles that slip out of sync as the\n  video goes on. Most tools only do a flat shift.\n\n## Who it's for\n\n**Video creators \u0026 YouTubers**, **marketers** (captioned social video),\n**educators**, **accessibility teams**, translators, and **developers** building\nmedia tooling who want a tiny subtitle library.\n\n## Install\n\n**No install —** just open the **[web app](https://didrod205.github.io/captionkit/)**.\n\n**Command line:**\n\n```bash\nnpx captionkit convert subs.srt --to vtt \u003e subs.vtt\nnpx captionkit shift subs.srt --ms 2000        # delay everything by 2s\nnpx captionkit scale subs.srt --factor 1.0427  # fix fps drift\n```\n\n**Library:**\n\n```bash\nnpm install captionkit\n```\n\nZero dependencies. ESM + CJS + TypeScript types. Runs in the browser, Node, Deno and Bun.\n\n## CLI\n\n```bash\ncaptionkit \u003cconvert|shift|scale|fix|info\u003e [file] [options]\ncat subs.srt | captionkit info\n```\n\nCommands: `convert` (`--to srt|vtt`), `shift` (`--ms`), `scale` (`--factor`),\n`fix` (`--gap`), `info`. Use `-o/--out` to write a file, or it prints to stdout.\n\n## Usage\n\n```ts\nimport { parse, convert, shift, resync, toVTT } from \"captionkit\";\n\n// Convert SRT → WebVTT (auto-detects the input)\nconvert(srtText, \"vtt\");\n\n// Parse, then re-time\nconst cues = parse(srtText);\nshift(cues, 2500);                 // everything 2.5s later\nshift(cues, -1000);                // …or earlier (clamped at 0)\n\n// Fix drift: map the first cue to 1.0s and the last to 10:00.0\nresync(cues, { firstStart: 1000, lastStart: 600000 });\n\ntoVTT(cues); // serialize back out\n```\n\n### More re-timing\n\n```ts\nimport { scale, fixOverlaps, totalDuration } from \"captionkit\";\n\nscale(cues, 25 / 23.976);   // framerate conversion\nfixOverlaps(cues, 40);      // no two cues overlap (40ms min gap)\ntotalDuration(cues);        // total on-screen time (ms)\n```\n\n## API\n\n| Function | Description |\n| -------- | ----------- |\n| `parse(text)` | Parse SRT or WebVTT → `Cue[]` (`{ index, start, end, text }`, ms). |\n| `toSRT(cues)` / `toVTT(cues)` / `convert(text, to)` | Serialize / convert. |\n| `detectFormat(text)` | `\"srt\"` or `\"vtt\"`. |\n| `shift(cues, ms)` | Offset all cues. |\n| `scale(cues, factor)` | Multiply all timestamps. |\n| `resync(cues, { firstStart, lastStart })` | Linear drift correction. |\n| `fixOverlaps(cues, minGap?)` | Remove overlaps. |\n| `renumber` / `totalDuration` | Helpers. |\n| `parseTimecode` / `formatSRT` / `formatVTT` | Timecode ⇄ ms. |\n\n## FAQ\n\n**Is my subtitle file uploaded anywhere?**\nNo. Everything runs on your device — no server, no telemetry, works offline.\n\n**What formats are supported?**\nSRT and WebVTT today. ASS/SSA and SBV are on the roadmap — [open an issue](https://github.com/didrod205/captionkit/issues).\n\n**What's the difference between shift, scale and resync?**\n*Shift* adds a fixed offset (captions are uniformly early/late). *Scale*\nmultiplies timestamps (framerate mismatch). *Resync* pins the first and last cue\nto target times and interpolates the rest — the fix for gradual drift.\n\n**Will it keep my line breaks and styling?**\nMulti-line cue text is preserved. Inline WebVTT tags pass through as text; full\nASS styling is not converted (yet).\n\n**Can I use it in a build or batch script?**\nYes — the library works in Node; map the functions over your files.\n\n## Contributing\n\nContributions welcome! See [CONTRIBUTING.md](./CONTRIBUTING.md) and the\n[Code of Conduct](./CODE_OF_CONDUCT.md).\n\n```bash\ngit clone https://github.com/didrod205/captionkit.git\ncd captionkit\nnpm install\nnpm test          # run the suite\nnpm run dev       # run the web app locally\n```\n\n## 💖 Sponsor\n\ncaptionkit is free, MIT-licensed, and built in spare time. If it saved you from\nhand-editing timestamps, please consider supporting it:\n\n- ⭐ **Star this repo** — free, and it genuinely helps others find it.\n- 🍋 **[Sponsor via Lemon Squeezy](https://elab-studio.lemonsqueezy.com/checkout/buy/5d059b89-51d0-456b-b33a-ed56994f7010)** — one-time or recurring support.\n\n**Where your support goes:** more formats (ASS/SSA, SBV, SAMI), split/merge,\ncharacters-per-second warnings, an auto-shift-by-waveform helper, a CLI,\nkeeping the free web app online, and fast issue responses.\n\n## License\n\n[MIT](./LICENSE) © captionkit contributors\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdidrod205%2Fcaptionkit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdidrod205%2Fcaptionkit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdidrod205%2Fcaptionkit/lists"}