Jon McGee
← All projects

Web app · Oct 2026 · Preview only

Clipper

A private tool for turning long YouTube videos into short vertical clips with word-by-word captions. The first stage, bringing a video in, works today. The rest is planned.

Preview only. This one isn't open for use.

What it is

Long videos hide short moments that work on their own. Clipper is the pipeline I am building to find them: add a creator, notice a new upload, download it, transcribe it, let an AI pick the best moments, render each one as a 9:16 clip with word-by-word captions, review it, and publish it.

It is a personal tool with one user, so it skips accounts and sign-in and spends the effort on the parts that have to be reliable.

What works today and what is planned

Built (milestone 1): the foundation and the ingest stage. You paste a YouTube link, a job is queued, a worker downloads the video's audio and saves it as a small FLAC file, and the dashboard shows every video and job as it moves along.

Planned: transcription, clip selection, rendering with captions, the clip library where I watch and edit clips, and publishing. The Clips tab in the preview is my design for the library. It uses invented clips and none of it exists in the app yet.

Walkthrough

The preview replays the stages below on a loop. Use Bigger preview to see it at full size.

  1. Paste a linkOne public YouTube video link goes into the box. Channels and playlists come in a later milestone.
  2. Watch it runThe job goes from Queued to Running to Done. The video appears in the list with its length and the saved audio file.
  3. Bad links are refusedOnly YouTube addresses pass the check, and it happens before anything is downloaded. A link to any other site is turned away and nothing is created.
  4. Failures say whyA video that is gone fails with a plain reason and a Retry button, instead of silently disappearing or looping forever.
  5. Rate limits wait, then retryIf YouTube asks for a bot check, the job backs off for a while and tries again by itself. The page shows how many attempts it has used.
  6. A crashed worker loses nothingIf the worker process dies mid-job, another worker notices the missed heartbeat and finishes the job. The result is still one video, never a duplicate.
  7. Browse the clip library (planned)Every rendered clip lands in a grid with its score and status. This and the next three steps are the planned design.
  8. Watch, trim and fix (planned)Open a clip to watch it with captions burned in, see why it was picked, drag its start and end by words, and fix a misheard word.
  9. Restyle and re-render (planned)Change the hook text, caption style and layout, then render again.
  10. Approve or reject (planned)Nothing is posted until I approve it.

Design decisions

  • A chain of small jobs: poll a creator, ingest, transcribe, select clips, render, review, publish. Each job reads its inputs, writes its outputs, queues the next job, and can be run twice safely.
  • The database is also the job queue. PostgreSQL hands each job to exactly one worker using row locks, so there is no separate message broker to run or break.
  • Failures are sorted into temporary, permanent and blocked. Each kind gets its own response: retry with a growing delay, stop and ask me, or wait out a rate limit.
  • Colour on the dashboard only ever means state, and it always comes with an icon and a word, so nothing depends on colour alone.
  • It never drives a social platform through a browser, and nothing is published until I approve the clip myself.

How it's built

  • Four Docker Compose services: the web dashboard, an API, a worker, and PostgreSQL. The API and worker are one Python codebase.
  • The dashboard is Next.js 16 with Tailwind CSS 4 and shadcn/ui on Radix. It only talks to the API and never touches the database.
  • Pure rules, such as which links are allowed, the retry schedule and error sorting, live in plain functions that never call out to other programs, so they are easy to test. The calls to yt-dlp and FFmpeg sit behind one small interface that the tests replace.
  • Every address is checked against an allow-list before it reaches the downloader, commands are passed as argument lists and never as strings, and secrets stay in environment variables.
  • The backend has 263 automated checks (153 unit and 110 database tests), plus browser tests for the dashboard that run against a stand-in API.

The preview above is a replay with invented videos and clips. The Ingest tab mirrors the working dashboard. The Clips tab is a design for a screen that has not been built.