Turn any video into structured documentation.
Vidistiller is a local-first, source-agnostic video-to-documentation engine. Paste a URL from YouTube, Vimeo, Twitch, X, Reddit, Rumble, or any direct MP4 link — Vidistiller distills the video down to what matters: the spoken words, the slides, the structure. Hours of watching become minutes of reading.
A completed job: a timestamped transcript, the video player, and captured snapshots side by side.
Every conversion is tracked in the jobs dashboard, with status, timestamps, and one-click access back into any past job.
The diagram below shows how every component connects at runtime.
┌──────────────────────────────────────────────────┐
│ User / Browser │
└──────────────────────┬───────────────────────────┘
│ HTTP (port 3000)
▼
┌──────────────────────────────────────────────────┐
│ web (Next.js Frontend) │
│ React + TypeScript · Tailwind CSS │
│ Submits video URLs, displays generated docs │
└──────────────────────┬───────────────────────────┘
│ HTTP REST (port 8000)
▼
┌──────────────────────────────────────────────────┐
│ api (FastAPI Backend) │
│ Receives requests · Validates input │
│ Queues background jobs · Returns results │
└───┬──────────┬──────────┬───────────────────────┘
│ │ │
┌──────────────┘ │ └──────────────┐
│ │ │
▼ ▼ ▼
┌────────────────────┐ ┌────────────────────┐ ┌────────────────────────┐
│ postgres (DB) │ │ redis (Cache + │ │ LLM (multi-provider) │
│ PostgreSQL 15 │ │ Message Broker) │ │ Ollama/OpenAI/Claude │
│ Stores all data: │ │ Redis 7 │ │ Generates structured │
│ jobs, videos, │ │ Caches results, │ │ documentation from │
│ transcripts, │ │ rate limits, │ │ transcripts │
│ snapshots, docs │ │ delivers tasks │ └────────────────────────┘
└────────────────────┘ │ to Celery │
└─────────┬──────────┘
│ task queue (Redis as broker)
▼
┌──────────────────────────────────────────────────┐
│ celery_worker (Background Tasks) │
│ Same codebase as api, different startup command │
│ │
│ ┌─────────────┐ ┌──────────────┐ │
│ │ Video │ │ Transcript │ │
│ │ Service │ │ Service │ │
│ │ (download) │ │ (Whisper) │ │
│ └─────────────┘ └──────────────┘ │
│ ┌─────────────┐ ┌──────────────┐ │
│ │ Snapshot │ │ LLM │ │
│ │ Service │ │ Service │ │
│ │ (FFmpeg) │ │ (Ollama) │ │
│ └─────────────┘ └──────────────┘ │
│ ┌─────────────────────────────┐ │
│ │ Slide Detection Service │ │
│ │ (SSIM + Tesseract OCR) │ │
│ └─────────────────────────────┘ │
└──────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────┐
│ pgadmin — Web UI for database administration (development only) │
│ Accessible at http://localhost:${PGADMIN_PORT} │
└──────────────────────────────────────────────────────────────────────────┘
1. User pastes a video URL in the frontend
2. Frontend sends POST request to the API
3. API validates the URL, creates a ProcessingJob in PostgreSQL, and pushes a task to Redis
4. Celery worker picks up the task from Redis and runs the pipeline:
a. Video Service → downloads the video and extracts metadata
b. Transcript Service → converts audio to text (via Ollama Whisper)
c. Snapshot Service → extracts key frames with FFmpeg
d. LLM Service → generates structured markdown/HTML documentation
5. Results are saved back to PostgreSQL
6. Frontend polls the API and displays the finished documentation
For presentation-style videos (tech talks, lectures, tutorials with slides), enable Presentation Mode to automatically detect and extract slides.
When Presentation Mode is toggled on before submitting a URL, the pipeline adds a slide detection phase after transcription:
- Layout Detection — Classifies the video as
full_frame,pip_speaker(picture-in-picture), orsplit_panel - SSIM Transition Scan — Compares consecutive frames using Structural Similarity Index to find slide changes
- LLM Ambiguity Classification — For borderline transitions (SSIM 0.85–0.93), the LLM classifies them as real transitions or incremental builds
- Slide Grouping — Merges transitions into distinct slides with enforced minimum duration (3s)
- Final State Capture — Extracts the last frame of each slide and saves it as a JPEG
- OCR — Runs Tesseract on each slide image to extract on-screen text
- Transcript Alignment — Maps spoken words to each slide by matching timestamp ranges
Each detected slide includes:
- Slide image — The final-state frame captured from the video
- OCR text — Text detected in the slide image (requires
tesseract) - Transcript text — What the speaker said during that slide's time window
- Timestamp range — When the slide appeared in the video
A good test case is a conference talk with slides, such as:
https://www.youtube.com/watch?v=aolI_Rz0ZqY
"So You Think You Know Git?" — Scott Chacon at FOSDEM 2024 Clean slide transitions, English captions, standard presentation layout.
This type of video typically produces 20–40 well-defined slides with OCR text and aligned transcript. Sports videos or vlogs are not good candidates — they have no actual slides, so every scene change gets flagged as a transition.
- Tesseract must be installed for OCR (
brew install tesseracton macOS,apk add tesseract-ocrin Docker) - Ollama is needed for LLM ambiguity classification (optional — only used for borderline transitions)
The Vidistiller UI supports three palettes switchable at runtime: Monokai (default, dark), Lunaris, and Nord. The active palette is persisted to localStorage as vidistiller-theme.
- Design File:
new_vidistiller_ui.pen— Complete Pencil design with all screens and Monokai colors - Design Specification:
docs/DESIGN_SPEC.md— Full design system details, colors, typography, and spacing
Palettes:
| Token | Monokai (default) | Lunaris | Nord |
|---|---|---|---|
| Primary | #BFFF3F (lime) |
#FF8400 (orange) |
#88C0D0 (blue) |
| bg-dark | #272822 |
#111111 |
#243353 |
| bg-light | #FDF6E3 |
#F2F3F0 |
#ECF0F4 |
Status colors (theme-independent):
- Success:
#22C55E| Warning:#FFB547| Destructive:#FF5C33
Typography:
- Sans: Arial, Helvetica
- Mono: Fira Code
To view or edit the design:
open new_vidistiller_ui.penThe design includes:
- 8 main screens (Home, Job Detail, Settings, Login, Register, Forgot Password, Reset Password, Dashboard)
- Component library with buttons, inputs, badges, cards
- Mobile views (iPhone 14)
- State variations (Loading, Error, Success)
The Python server that powers the entire application. Built with FastAPI, it:
- Receives HTTP requests from the frontend (submit URL, check job status, fetch results).
- Validates input using Pydantic schemas (
schemas.py,models.py). - Persists data via SQLAlchemy ORM models in
db/models.py, connected through the session factory indb/session.py. - Queues background work by dispatching Celery tasks (defined in
tasks.py) through Redis. - Exposes interactive docs automatically at
/docs(Swagger UI).
Configuration is centralized in core/config.py and loaded from the .env file.
The user-facing web application. Built with Next.js (App Router), React, TypeScript, and Tailwind CSS.
app/uses file-based routing: each folder becomes a URL path.[id]is a dynamic segment (e.g.,/jobs/abc-123).components/contains the UI building blocks — a form to submit URLs, progress bars, transcript viewers, image galleries, and document renderers.services/api.tsandlib/api.tshandle all HTTP communication with the backend.hooks/provides custom React hooks for data fetching and polling job status.
Self-contained Python modules that encapsulate the core processing logic, all located in backend/app/services/.
| Service | What it does |
|---|---|
video.py |
Downloads the video, extracts metadata (title, channel, duration, thumbnail), resolves source type |
transcript.py |
Sends audio to Whisper, returns timestamped text segments |
snapshot.py |
Uses FFmpeg to extract key frames at configurable intervals |
llm.py |
Sends transcript + snapshots to LLM (Ollama, OpenAI, or Anthropic), returns structured documentation |
Alembic manages database schema changes. Each file in versions/ is a migration script with upgrade() and downgrade() functions, ensuring every environment (local, CI, production) has an identical database schema.
Pytest test files. conftest.py provides shared fixtures (test database sessions, mocked services). Run the full suite with pytest tests/.
Vitest + React Testing Library tests for React components and utilities. Run with cd frontend && npm test.
Typed settings classes for database, JWT, CORS, logging, and every other configurable piece of the app. Reads from environment variables (see .env.example) with validation and sane development defaults, so docker compose up -d boots without any required configuration.
Automation helpers for common tasks:
deploy.sh— deploy the stack to a configured hostpush-backend.sh— build and push the backend imagesetup-staging.sh— bring up a staging environment
Terraform definitions for deploying the application to a self-hosted Proxmox VM. Defines the VM, network, and cloud-init configuration; outputs connection details after terraform apply. See deploy/ for the companion Ansible playbooks.
A GitHub Actions pipeline template for automated testing and deployment on push/PR.
The project includes three Compose files, each serving a different purpose:
The main file. Starts all 6 services needed for local development:
| Service | Role |
|---|---|
postgres |
PostgreSQL 15 database |
redis |
Cache + Celery message broker |
api |
FastAPI backend (port 8000) |
celery_worker |
Background task processor |
web |
Next.js frontend (port 3000) |
pgadmin |
Database admin UI |
docker compose up -d # start everything
docker compose logs -f api # follow API logs
docker compose down # stop everythingA lightweight file that spins up only postgres and redis with hardcoded credentials. It exists so you can run pytest on your host machine (or in CI) against real database and cache instances, without starting the full stack.
Key differences from the main file:
- 2 services instead of 6 (no api, celery, web, or pgadmin)
- Hardcoded credentials (
tutorial_user/tutorial_password) so tests are reproducible without a.envfile - Faster health checks (5s interval vs 10s) for quicker test startup
docker compose -f docker-compose.test.yml up -d # start test infra
pytest tests/ # run tests against real postgres + redis
docker compose -f docker-compose.test.yml down # tear downAutomatically merged with docker-compose.yml by Docker Compose. Use this for personal developer customizations (extra port mappings, debug flags, volume overrides) without touching the shared main file.
- Docker 24+ with Compose v2 (
docker compose version) - Node.js 18+ (for running the frontend outside Docker)
- Python 3.12+ (for running the backend outside Docker)
- Tesseract OCR — required for Presentation Mode (
brew install tesseracton macOS)
-
Clone the repository and copy the environment template:
cp .env.example .env # Edit .env with your local values -
Start all services:
# Optional: remove all containers and images first docker compose down --rmi all # Start all services docker compose down && \ docker compose up -d && \ sleep 60 && \ docker compose ps
-
Run database migrations:
docker compose exec api alembic upgrade head -
Open the app:
- Frontend:
http://localhost:3000 - API docs:
http://localhost:8000/docs - pgAdmin:
http://localhost:5050
- Frontend:
See ops-runbook for troubleshooting and VM_DEPLOYMENT.md for production setup.
If you just want to preview the UI in your browser without starting the full stack:
cd frontend && npm install && npm run devThe dev server starts at http://localhost:3000. API calls will fail without the backend running.
docker compose up -d --no-deps webThe --no-deps flag tells Compose to start only the web container and ignore its depends_on chain (api -> postgres + redis). The UI will render but API requests will fail without the backend.
Note: Running
docker compose up -d web(without--no-deps) will also startapi,postgres, andredisautomatically because of the dependency chain defined indocker-compose.yml.
If you want to work on the API without starting the frontend, Celery worker, or pgAdmin:
You still need PostgreSQL and Redis running (either locally installed or via the test compose file):
# Start only the database and cache
docker compose -f docker-compose.test.yml up -d
# Install Python dependencies
cd backend && pip install -r requirements.txt
# Run database migrations
alembic upgrade head
# Start the FastAPI dev server with auto-reload
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000The API will be available at http://localhost:8000 with interactive docs at http://localhost:8000/docs.
docker compose up -d apiThis starts api, postgres, and redis (because api depends on both being healthy), but does not start web, celery_worker, or pgadmin.
docker compose up -d api celery_workerThis starts api, celery_worker, postgres, and redis — everything needed for the full backend pipeline without the frontend or pgAdmin.
Note: Without the
celery_worker, the API can still receive requests and create jobs, but no background processing (video downloads, transcription, document generation) will happen.


