# The hundredth clip costs nothing: Klipkit, a local-first clip finder for freelance editors

> Source: <https://dev.to/hendrahend/the-hundredth-clip-costs-nothing-klipkit-a-local-first-clip-finder-for-freelance-editors-5891>
> Published: 2026-10-18 03:03:30+00:00

*This is a submission for the [Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle](https://dev.to/challenges/hacktoberfest-week2-2026-10-12)*

**Klipkit** is a clip finder for freelance short-form editors that runs entirely on a laptop — no upload, no per-minute API bill, no NDA problem.

Point it at a podcast, a stream VOD or a client's raw footage. It transcribes the audio locally, finds the moments that work as standalone clips, writes a hook line for each, and exports a client-ready pack: MP4 + `.srt` + a run sheet with timecodes.

```
source ──► whisper (local) ──► candidate moments ──► local LLM judge ──► clip pack
file/URL     word timestamps     cheap heuristics      open-weight model     mp4 + srt
```

**Who would pay for it:** freelance clippers and short-form editors who get paid per clip and work under NDA. They have two problems that hosted tools make worse.

Per-minute pricing: cloud clip tools charge by upload-minute. If a client sends a two-hour podcast and you pull eight clips, you pay for the whole two hours. Do that for ten clients and the tooling costs more than the job.

And the footage isn't theirs. Clients send raw footage under NDA — uploading it to a server the client doesn't control is a non-starter, and "we have a SOC 2 report" is not a satisfying answer when the contract says the file never leaves the editor's machine.

So the business only works if the marginal cost of the hundredth clip is zero. That requires local inference, which requires open weights.

No hosted link, and that's the point — Klipkit runs locally, so there is nothing to deploy. Here is a real run on a public 62-minute Indonesian-language talk video, on an RTX 2050 with 4 GB of VRAM:

``` bash
$ klipkit run "https://www.youtube.com/watch?v=zOnA_8JmdM8" -n 5 --whisper small --llm gemma3:1b

  Top moments - NEPHILIM, NABI ADAM & RAKSASA PURBA...
┏━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━┳━━━━━━━┳━━━━━━┓
┃ # ┃ Start    ┃ End      ┃ Len ┃ Score ┃ Hook ┃
┡━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━╇━━━━━━━╇━━━━━━┩
│ 1 │ 00:02:45 │ 00:03:31 │ 46s │  0.91 │ -    │
│ 2 │ 00:03:37 │ 00:04:23 │ 46s │  0.91 │ -    │
│ 3 │ 00:14:16 │ 00:15:05 │ 49s │  0.91 │ -    │
│ 4 │ 00:19:45 │ 00:20:31 │ 46s │  0.91 │ -    │
│ 5 │ 00:49:41 │ 00:50:31 │ 50s │  0.91 │ -    │
└───┴──────────┴──────────┴─────┴───────┴──────┘

Client-ready pack: out/nephilim/pack
```

*Test source is a public YouTube video, used only as input. Klipkit processed it locally — nothing was re-uploaded, and the rendered clips stay on my machine.*

| Stage | Result | 
|---|---|
| Transcribe | 9,612 words, language `id` at 0.99 confidence, ~6 min on GPU | 
| Utterances | 1,216, reassembled from word timings | 
| Candidates | 606 → 16 shortlisted by heuristics | 
| Output | 5 MP4 clips (45–50 s), 5 SRT files, a run sheet | 

Roughly ten minutes of wall clock, six of it transcription. The SRT timings come out of the *same* word timings used for cutting, so captions stay locked to the audio instead of drifting.

The empty `Hook` column is the honest part of this demo, and I get into it below.

**Find the clips worth posting — on your own laptop, with no cloud in the loop.**

Klipkit is a local-first clip finder built for freelance clippers and short-form video editors
Point it at a podcast, a stream VOD or a client's raw footage; it transcribes the audio with
Whisper, finds the moments that actually work as standalone clips, writes a hook line for each
and exports a client-ready pack (MP4 + `.srt` captions + a notes sheet).

Nothing leaves your machine. No upload, no per-minute API bill, no NDA problem.

```
                                                    
  source ──► whisper (local) ──► candidate moments ──► local LLM judge ──► clip pack
  file/URL     word timestamps     cheap heuristics      open-weight model     mp4 + srt
```

Freelance clippers are paid per clip, so two things kill their margin: **time** and **API cost**
Cloud clip tools charge per upload-minute, which makes high-volume work unprofitable, and they…

MIT licensed. Python, with `faster-whisper`, Ollama, `ffmpeg` and `yt-dlp` as the only real dependencies.

The open-source AI is the product, not a decoration. Four open pieces do all the work:

| Piece | Role | Why it has to be open | 
|---|---|---|
| **faster-whisper** | transcription with word-level timestamps | runs offline, no per-minute fee, audio never leaves the disk | 
| **Ollama + Gemma 3** | judges which moments are clip-worthy, writes the hooks | runs on a laptop GPU, swappable, free forever | 
| **ffmpeg** | cutting and caption rendering | already on every editor's machine | 
| **yt-dlp** | optional URL import | no vendor gatekeeping your sources | 

The design decision that mattered most: **the model never sees the whole transcript.**

An hour of talk audio is ~10,000 words. Feeding that to a small open-weight model in one shot produces garbage — it loses the thread and burns context on filler. So the pipeline layers cheap and expensive. Heuristics run first and cost nothing: overlapping 45-second windows are scored on question openers, digits, filler density, and whether the cut starts mid-thought. Only the top slice reaches the model.

``` python
# klipkit/text.py
def rank_candidates(candidates, keep_ratio=0.5, min_keep=8, max_keep=16):
    ranked = sorted(candidates, key=lambda c: c.heuristic, reverse=True)
    keep = max(min_keep, int(len(candidates) * keep_ratio))
    keep = min(keep, max_keep, len(ranked))
    return sorted(ranked[:keep], key=lambda c: c.start)
```

That cap does real work: 606 candidates became 16, which is four small batches of judging instead of 606. Ollama runs the judge with `format: "json"` so the model is constrained to a parseable reply, and the whole pipeline ran on a 4 GB laptop GPU.

**What broke on the way** — the parts I'd want to read before trying this myself:

**1. yt-dlp now requires a JavaScript runtime.** Since the 2026 releases, YouTube extraction needs one, and it degrades with a vague "some formats may be missing" warning instead of an error. Node was already installed; the Python API just wasn't being told about it.

```
# klipkit/ingest.py
opts = {
    "format": "bv*[height<=720][ext=mp4]+ba[ext=m4a]/b[height<=720]/bv*[height<=720]+ba/b",
    "js_runtimes": {"node": {}},   # without this, YouTube extraction silently degrades
}
```

**2. `av` 19 broke faster-whisper.** Not a warning — a hard crash on every input, because `av.open()` gained a `metadata_errors` keyword that faster-whisper 1.2.1 doesn't pass. The fix belongs in `pyproject.toml`, not a README footnote:

```
dependencies = [
    "faster-whisper>=1.1",
    "av<15",
    "yt-dlp>=2025.1.1",
]
```

**3. cuBLAS wasn't on `LD_LIBRARY_PATH`.** faster-whisper picked CUDA via `device="auto"`, model construction succeeded, then encoding failed with `Library libcublas.so.12 is not found`. The library existed — bundled inside Ollama's own runtime. Pointing `LD_LIBRARY_PATH` at it took the GPU to 98% utilization. Worth knowing before concluding your GPU is unsupported.

**4. Gemma 3 1b is too small to batch-score reliably.** This is the empty Hook column. I asked it to score four candidates at once and return JSON; it returned verdicts for one or two, and one batch came back as an HTTP 500 from Ollama. Every clip fell back to heuristic scoring.

That's a model-capability limit, not a pipeline bug. The default is `gemma3:4b` for a reason — I only had the 1b because the Ollama registry was unreachable from this machine and I imported the GGUF from HuggingFace directly. It's also the clearest argument for the swappable-model design: the fix is a flag, not a rewrite.

Three things about this project only exist because the pieces are open.

**The marginal cost is actually zero.** A closed API that charges per minute makes high-volume freelance work unprofitable. Running Gemma 3 locally means the hundredth clip costs the same as the first: nothing. That isn't a discount, it's the difference between a tool a freelancer can build a business on and a tool that eats their margin.

**The client's footage never leaves the laptop.** This is a hard requirement, not a preference. No closed API can satisfy an NDA clause that says the file stays on the editor's machine — no matter how good its privacy policy is. Local open-weight inference is the only architecture that makes the promise structurally true instead of contractually promised.

**I can swap the brain without touching the pipeline.** `--llm gemma3:1b` for a fast pass, `gemma3:4b` when a clip really matters, or something else entirely next month. When I discovered the 1b model couldn't batch-score, the fix was one flag. With a hosted API, model quality is a vendor decision I'd have to wait on.

The open pieces are also what let a 1b model and a 4 GB GPU be enough. I'm not paying per token, so layering a free heuristic prefilter in front of an expensive judge is pure upside — 606 candidates down to 16 before the model sees anything.

**Best Use of Gemma** — Gemma 3 is the judge at the centre of the pipeline, not a bolt-on. It scores hook strength, self-containment, payoff and energy for each shortlisted moment and writes the on-screen hook line. The project ships with `gemma3:4b` as the default and treats the model as swappable, so the same code runs a 1b or a larger Gemma depending on the hardware and how much the clip matters.

The local-LLM stack I leaned on is well-trodden on DEV, and these shaped how I built it:

**Score one candidate at a time and accept the latency.** Batching looked like an obvious token saving, but a 1b model can't hold four scoring rubrics in its head at once, and a partial JSON response is worse than a slow complete one — the fallback silently degrades quality instead of failing loudly.

**Make the fallback visible.** "scored by heuristics only" is currently a note in the run sheet. It should be a warning at the top, because a pack with five empty hook columns looks like a working result.

**Test on a short source first.** My first real test was 62 minutes, so every iteration cost six minutes of transcription. A three-minute clip would have caught the `av` crash in seconds.

Run it yourself: `klipkit doctor` tells you what's missing before you spend six minutes finding out.
