Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
39ca1db
chore: update gitignore and project configuration
ollisulopuisto Dec 9, 2025
07fbe4b
feat: Introduce Storyteller caption style with multi-line text format…
ollisulopuisto Dec 9, 2025
705d4d7
feat: Add task cancellation, video frame extraction, transcription, a…
ollisulopuisto Dec 10, 2025
611406b
feat: Refine caption line wrapping minimum width calculation and enha…
ollisulopuisto Dec 10, 2025
c570786
feat: Add Textarea component, support local and MOV video file loadin…
ollisulopuisto Dec 10, 2025
cb12746
fix: Remove redundant percentage from export status display.
ollisulopuisto Dec 10, 2025
698b940
feat: add video poster support to the caption editor and refine activ…
ollisulopuisto Dec 10, 2025
c5937fa
vertical pos adjust
ollisulopuisto Dec 10, 2025
07647df
feat: Add backend-driven caption layout preview to the editor, utiliz…
ollisulopuisto Dec 11, 2025
44c6fd4
fix: Round segment and word timestamps to the nearest millisecond for…
ollisulopuisto Dec 20, 2025
b96b7bb
feat: proportionally distribute time spans for hyphenated tokens base…
ollisulopuisto Dec 20, 2025
a367e71
feat: Implement saving and loading of adjusted captions to video-spec…
ollisulopuisto Dec 20, 2025
19eed94
feat: Round segment and word timestamps to integers for backend calls…
ollisulopuisto Dec 20, 2025
4057f30
feat: Implement dynamic caption previews for templates by adding a ne…
ollisulopuisto Dec 22, 2025
5137d51
feat: Enhance video format support, reflect current caption settings …
ollisulopuisto Dec 22, 2025
7d780e6
refactor: remove video playback and live caption preview from Caption…
ollisulopuisto Dec 22, 2025
6f191f3
FFmpeg missing ASS
ollisulopuisto Jan 28, 2026
9c54d53
feat: Add FFmpeg libass availability check and a subtitle test asset.
ollisulopuisto Jan 28, 2026
be71f07
buncha font work
ollisulopuisto Jan 28, 2026
9d4742d
Add CI/CD release pipeline
ollisulopuisto Jan 28, 2026
76fe04b
readme updates
ollisulopuisto Jan 28, 2026
ceea061
buncha cleanup pre-testing
ollisulopuisto Jan 28, 2026
2158b6c
testing
ollisulopuisto Jan 28, 2026
c2e7f57
fix failing test
ollisulopuisto Jan 28, 2026
ba3aade
chore: update bun.lock and fix lint setup to resolve CI error
ollisulopuisto Jan 29, 2026
41eff02
CI/CD fixes
ollisulopuisto Jan 29, 2026
b083370
chore: bump version to 1.0.3
ollisulopuisto Jan 29, 2026
1081cce
chore: update release workflow and bump version to 1.0.4
ollisulopuisto Jan 29, 2026
c9b5f54
fix(ci): scope linux ffmpeg extraction to avoid permission errors and…
ollisulopuisto Jan 29, 2026
0fefb53
fix(build): disable code signing for macos ci builds and bump version…
ollisulopuisto Jan 29, 2026
437e060
del old file
ollisulopuisto Jan 30, 2026
1747e7d
fix: address PR review comments from Copilot
ollisulopuisto Jan 30, 2026
8236a19
chore: bump version to 1.0.7
ollisulopuisto Jan 30, 2026
a7c736b
ci: add electron build check to CI workflow
ollisulopuisto Feb 13, 2026
5a18016
chore: bump version to 1.0.8
ollisulopuisto Feb 13, 2026
bf3cf2e
ci: fix mac build by adding arch to artifact name and separating plat…
ollisulopuisto Feb 13, 2026
1ff7aa7
feat: add manual preview refresh button to CaptionEditor
ollisulopuisto Mar 12, 2026
60cd91e
chore: exclude large models from bundle to reduce binary size
ollisulopuisto Mar 12, 2026
9218042
chore: bump version to 1.0.9
ollisulopuisto Mar 12, 2026
ffb53e1
fix: use reliable FFmpeg source for Linux builds
ollisulopuisto Mar 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: CI

on:
push:
branches: [main, master]
pull_request:
branches: [main, master]

jobs:
lint-and-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup Rust
uses: dtolnay/rust-toolchain@stable

- name: Rust lint
working-directory: rust
run: cargo clippy

- name: Rust build check
working-directory: rust
run: cargo build --release

- name: Setup Bun
uses: oven-sh/setup-bun@v2

- name: Install Electron dependencies
working-directory: electron
run: bun install --frozen-lockfile

- name: Lint Electron app
working-directory: electron
run: bun run lint

- name: TypeScript check
working-directory: electron
run: bunx tsc --noEmit

- name: Build Electron app
working-directory: electron
run: bun run vite:build:app
115 changes: 115 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
name: Release

on:
push:
tags:
- 'v*'

permissions:
contents: write

jobs:
build:
strategy:
fail-fast: false
matrix:
include:
- os: macos-latest
rust_target: aarch64-apple-darwin
electron_args: --mac --arm64
artifact_name: mac-arm64
- os: macos-latest
rust_target: x86_64-apple-darwin
electron_args: --mac --x64
artifact_name: mac-x64
- os: windows-latest
rust_target: x86_64-pc-windows-msvc
electron_args: --win --x64
artifact_name: win-x64
- os: ubuntu-latest
rust_target: x86_64-unknown-linux-gnu
electron_args: --linux --x64
artifact_name: linux-x64

runs-on: ${{ matrix.os }}

steps:
- uses: actions/checkout@v4

- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.rust_target }}

- name: Build Rust core
working-directory: rust
run: cargo build --release --target ${{ matrix.rust_target }}

- name: Copy Rust binary (Unix)
if: runner.os != 'Windows'
run: cp rust/target/${{ matrix.rust_target }}/release/core rust/target/release/core

- name: Copy Rust binary (Windows)
if: runner.os == 'Windows'
run: |
New-Item -ItemType Directory -Force -Path rust/target/release
Copy-Item rust/target/${{ matrix.rust_target }}/release/core.exe rust/target/release/core.exe

- name: Download platform binaries
run: bash scripts/download-binaries.sh
env:
TARGET_OS: ${{ runner.os }}

- name: Setup Bun
uses: oven-sh/setup-bun@v2

- name: Install dependencies
working-directory: electron
run: bun install

- name: Build Electron app
working-directory: electron
run: bun run vite:build:app

- name: Package for distribution
working-directory: electron
run: bunx electron-builder ${{ matrix.electron_args }} --publish never
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CSC_IDENTITY_AUTO_DISCOVERY: false

- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: capslap-${{ matrix.artifact_name }}
path: |
electron/dist/*.dmg
electron/dist/*.exe
electron/dist/*.AppImage
electron/dist/*.deb
electron/dist/*.snap
if-no-files-found: ignore

release:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
merge-multiple: true

- name: List artifacts
run: find artifacts -type f | head -20

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
files: artifacts/*
generate_release_notes: true
draft: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
27 changes: 26 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,30 @@ rust/models/*.bin

rust/bin/ffmpeg
rust/bin/ffprobe
rust/bin/whisper-cli*
rust/bin/lib/
rust/bin-win/
rust/bin-linux/
.DS_Store
rust/bin/lib/libinternal/cmake/
rust/bin/lib/libinternal/cmake/
rust/deps/
rust/target/
rust/bin/test_input.mp4
rust/bin/lib/libinternal/libggml-base.0.9.4-dirty.dylib
rust/bin/lib/libinternal/libggml-blas.0.9.4-dirty.dylib
rust/bin/lib/libinternal/libggml-cpu.0.9.4-dirty.dylib
rust/bin/lib/libinternal/libggml-metal.0.9.4-dirty.dylib
rust/bin/lib/libinternal/libggml.0.9.4-dirty.dylib
rust/bin/lib/libinternal/*.dirty.dylib
rust/bin/lib/libinternal/*.0.dylib
rust/bin/lib/libinternal/*.coreml.dylib
rust/bin/lib/libinternal/*.1.8.2.dylib
*.dirty.dylib
*.0.dylib
*.coreml.dylib
*.1.8.2.dylib
electron/out/
electron/dist/
node_modules/
electron/package-lock.json
rust/check_output.txt
55 changes: 47 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,30 @@
# CapSlap - AI Video Caption Generator

Automatically generate and burn captions into videos using AI transcription.

<img width="1200" height="800" alt="CleanShot 2025-10-25 at 13 28 49@2x" src="https://github.com/user-attachments/assets/58b2db2a-e2dd-433a-bf2e-5d8c19efad93" />

Automatically generate and burn captions into videos using AI transcription. **Works 100% offline** with local Whisper models.

![CapSlap Screenshot](screenshot.png)

## ✨ Features

- **🔒 100% Local/Offline** - All transcription runs locally using whisper.cpp. No cloud API required, your video never leaves your machine
- **👁️ Live Preview** - See exactly how your captions will look before exporting with real-time preview
- **🎨 30+ Custom Fonts** - Choose from a curated library organized by style:
- **Modern / Sans** - Montserrat, Roboto, Open Sans, Lato, Raleway, Kanit, Poppins, WorkSans
- **Display / Impact** - THEBOLDFONT, Bebas Neue, Anton, Lilita One, Oswald, Bangers
- **Fun / Comic** - Komika Axis, Comic Neue, Fredoka, Chewy, Luckiest Guy
- **Serif / Elegant** - Playfair Display, Merriweather, Lora, Cinzel, Bodoni Moda
- **Handwritten / Script** - Permanent Marker, Patrick Hand, Amatic SC, Caveat Brush, Pacifico
- **📐 Adjustable Font Size** - Fine-tune caption size to fit your video style
- **🎬 Caption Templates** - Pre-built styles: Oneliner, Karaoke, Vibrant, Storyteller
- **🎯 Word-Level Timing** - Karaoke-style highlighting with precise word synchronization
- **📱 Export Formats** - 9:16 (TikTok/Reels), 16:9 (YouTube), 1:1 (Instagram), 4:5 (Feed posts)
- **🎥 Wide Format Support** - Works with MP4, MOV, WebM, AVI, WMV, FLV, MKV, MPEG, 3GP, and more
- **✏️ Caption Editor** - Full control over your captions before exporting:
- Edit transcribed text with live preview
- Shift words between segments to fix line breaks
- View and adjust word-level timing
- Double-click any segment to edit text directly
- **🌍 Multilingual** - Transcribe in 99+ languages including English, Spanish, French, German, Finnish, Japanese, and more

## Prerequisites

Expand Down Expand Up @@ -43,9 +64,9 @@ Automatically generate and burn captions into videos using AI transcription.
bun run dev
```

## Whisper Models
## Whisper Models (Local Transcription)

Local whisper models can be downloaded directly through the app UI, or manually:
All transcription runs **100% locally** using whisper.cpp. Download models directly through the app UI, or manually:

```bash
mkdir -p rust/models
Expand All @@ -54,12 +75,30 @@ mkdir -p rust/models
curl -L https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-tiny.bin \
-o rust/models/ggml-tiny.bin

# Base model (recommended, 142 MB)
# Large v3 Turbo ⭐ RECOMMENDED (best speed/accuracy balance, 809 MB)
curl -L https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-large-v3-turbo.bin \
-o rust/models/ggml-large-v3-turbo.bin

# Base model (lightweight, 142 MB)
curl -L https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-base.bin \
-o rust/models/ggml-base.bin

# Small model (466 MB)
curl -L https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-small.bin \
-o rust/models/ggml-small.bin
```

**No API key required!** OpenAI API is available as an optional fallback if you prefer cloud transcription.

## Adding More Fonts

Additional fonts can be downloaded using the included script:

```bash
./scripts/download_fonts.sh
```

Alternatively, use OpenAI API (requires API key) without downloading models.
This downloads fonts from Google Fonts to `rust/src/fonts/`.

## Platform-Specific Notes

Expand Down
Loading