Tutorial

Your overnight batch, triaged before you wake.

The morning after costs more than the GPU hours. You queue 40 takes of a shot overnight, and current models being what they are, a handful survive. Finding that handful means scrubbing every clip by hand: the black renders, the frozen frames, the take where a hand grows a sixth finger at the exact moment it grips the cup. An hour of scrubbing to find six minutes of footage.

That hour is now a command. dailies is an open-source tool we built and released this week (MIT, Python, zero dependencies beyond ffmpeg) that reviews a batch the way a strict assistant editor would: it kills the dead takes, with timestamped reasons, and ranks the survivors so you watch the best candidates first. By the end of this tutorial you will have reviewed a folder of takes, read a ranked report of the survivors, and left a watcher running that builds that report while you sleep. Ten minutes, four steps.

The short version
  • pip install video-dailies, then dailies review ./takes. Dead takes get killed with reasons; survivors get ranked per shot.
  • dailies report writes one static HTML page: hover to scrub, defects marked on a timeline.
  • dailies watch reviews takes as your queue produces them, so the report exists before you wake up.
  • An optional vision pass screens survivors with any vision model behind an OpenAI-compatible endpoint, using rules you write yourself.

Before you start

You need three things, and the fourth step has one optional extra:

  • Python 3.9 or newer. Check with python3 --version.
  • ffmpeg. Check with ffmpeg -version. If that fails: brew install ffmpeg on a Mac, sudo apt install ffmpeg on Linux.
  • A folder of generated clips. Any mp4, mov, webm, mkv, or avi files. A ComfyUI output folder works as is.
  • Optional, for step 4: access to a vision model, either an API key or a local GPU.

Two words from film sets, because the tool borrows them. A take is one generated clip, one attempt. A shot is the thing you are attempting, so 40 takes of shot 7 are 40 tries at the same moment of your film. On a film set, "dailies" is the ritual of reviewing yesterday's footage. Same job here.

One promise before the commands: dailies does triage, not judgment. Vision models are still mediocre at fine-grained artifact detection (Artifact-Bench, May 2026), so the tool never claims a take is good. It claims that 34 of your 40 takes are definitely dead, shows you why with timestamps, and hands you the surviving six in ranked order. Taste stays your job; dailies takes the scrubbing.

Step 1: install it

pip install video-dailies

Check it worked:

$ dailies --version
0.1.0

Step 2: review a batch

We generated an example batch for this tutorial so you can see the whole loop on actual footage. The model is MiniMax Hailuo 2.3, a current-generation budget tier, exactly the kind of model an overnight batch runs on. The prompt is built from things every video model still gets wrong: finger work (cracking an egg one-handed), object contact (a stirring spoon), and a chalkboard sign that must read exactly "HAPPY HOUR 5-7PM". Four takes of 6 seconds, one prompt, no editing:

The tutorial batch: MiniMax Hailuo 2.3, one prompt, no editing. Play any take and watch the sign. Files and review results ship with this page.
The exact prompt
A close-up of a bartender's hands cracking an egg one-handed over a
cocktail shaker while the other hand stirs with a spoon, then both
hands lift a chalkboard sign with the handwritten text "HAPPY HOUR
5-7PM", busy bar background, one continuous shot

Point review at the folder. This is the output of the run on exactly these four files:

$ dailies review ./takes
reviewed 4 takes, killed 0
  review   #1  take-003.mp4
  review   #2  take-001.mp4
  review   #3  take-004.mp4
  review   #4  take-002.mp4

Each line is one take. review means the take survived every check, and #1 is its rank within the shot: fewest defects and least flicker first. When a take dies, the line says kill and ends with the evidence, like black for 4.8s of 5.0s or frozen for 3.1s of 5.0s; the other mechanical kill triggers are decode errors and a scene cut in the middle of what should be one continuous shot (that cut means the model glitched, not the editor). Folder names become shot names automatically.

But look at the output again: zero kills, and now play any take and watch the chalkboard. Every take in this batch has something wrong with it. The mechanical stage catches broken files, not broken content, and that is the limit of stage 1. Hold that thought; the vision pass in the last step settles it.

Where did the results go? Next to each clip there is now a small JSON file, called a sidecar, named take-031.mp4.take.json. It holds everything the review found:

{
  "take_id": "sha256:9f2c81...",
  "shot": "shot-07",
  "review": {
    "mechanical": {
      "freeze": [{"start": 1.9, "end": 5.0}],
      "kill_reasons": ["frozen for 3.1s of 5.0s"]
    },
    "verdict": "kill",
    "rank_in_shot": 13
  }
}

The take_id is a hash of the clip's content, which makes re-running free: run dailies review again and already-reviewed takes are skipped, only new clips pay. The format is a published spec, so other tools can read and extend the same file.

Step 3: read the report

The terminal output is for skimming. The reviewing happens on one HTML page:

dailies report ./takes -o report.html

Open report.html in a browser, straight from disk, no server needed. You should see your takes grouped by shot, survivors first. Hover any clip and it scrubs; click and it plays. Under each take a small timeline marks where the problems are: gray spans for black video, blue for frozen, ticks for stray cuts. Under each killed take, "why killed" expands the reasons. Morning routine: open the page, watch the top one or two survivors per shot, done.

Each batch gets its own report. Here is the bar batch's, unedited; once the vision pass has run, its defects appear as dots on the timelines, color-coded by family: red for anatomy, orange for physics, purple for morphing, yellow for text, teal for background, magenta for adherence.

Step 4: leave a watcher running overnight

So far you reviewed a finished batch. The watcher reviews takes while the batch is still rendering. Start your generation queue as usual, and in a second terminal:

$ dailies watch ~/ComfyUI/output --report report.html
watching ~/ComfyUI/output every 5s; ctrl-c to stop
23:41:09  review   shot-07/take-031.mp4
23:44:52  kill     shot-07/take-032.mp4  black for 4.8s of 5.0s

Every time a clip lands, the watcher waits for the encoder to finish writing it, reviews it, and rebuilds the report. Go to bed. The report is current when you wake up. Two details worth knowing: restarting the watcher never re-reviews old takes, and --json switches the output to one JSON line per take, for piping into your own scripts.

Optional: add the vision pass

Everything so far ran on your CPU and caught the cheap deaths. Some failures need eyes: a hand with six fingers, a held object drifting off the grip, a camera direction the model ignored. For those, dailies can send frames from each surviving take to a vision model and ask it structured questions. It works with any vision model behind an OpenAI-compatible API, which in practice means two options.

Hosted, the fastest to try. Put your provider's key in the environment and point --vlm at their endpoint, with their Qwen3-VL model id:

export DAILIES_VLM_KEY=sk-...
dailies review ./takes --vlm https://your-provider.example/v1 --vlm-model qwen3-vl

Local, free after the download, if you have the GPU: serve Qwen3-VL with vLLM or llama.cpp, then use --vlm http://localhost:8000/v1. Either way the same flags work on watch, so the overnight run gets the vision pass too. Frames are chosen for the model, not sampled evenly: defects cluster at sudden frame-to-frame changes, and the mechanical stage already found those moments.

The questions the model is asked live in a rubric. The built-in rules cover hands, faces, limbs, object morphing, object continuity, contact physics, motion physics, background stability, text legibility, and prompt adherence; the full checklist is two sections down. This is also the part you will want to make your own: a rule is a prompt plus an optional kill threshold. Save this as film.json:

{"rules": {
  "brand.no_text": {
    "prompt": "Is there any legible text or watermark in these frames?",
    "fail_at": 3
  },
  "continuity.wardrobe": {
    "prompt": "The character wears a red coat in every shot. Report frames where the coat changes color or vanishes."
  }
}}

Run with --rubric film.json. The model rates each defect it finds from 1, barely visible, to 5, unusable. A rule with fail_at kills any take at or above that severity; a rule without one never kills, it only pushes a take down the ranking. Wardrobe continuity, a prop that must survive the scene, a client's no-watermark rule: each is three lines in this file, no code.

Time to settle the bar batch from step 2. Its four takes passed every mechanical check. The vision pass, running Gemini 3.5 Flash Lite behind an OpenAI-compatible endpoint with this rubric (the default rules plus a three-line text.legibility rule for the sign), kills all four. An excerpt of the run:

$ dailies review ./bar --vlm https://api.gmi-serving.com/v1 \
    --vlm-model google/gemini-3.5-flash-lite --rubric bar-rubric.json
reviewed 4 takes, killed 4
  kill  #1  take-003.mp4  continuity.objects severity 5 at 3.875s:
        The egg and spoon instantly vanish, replaced by a chalkboard sign
        text.legibility severity 4 at 3.875-4.333s:
        Sign text reads HAPPY HOUR 5-5PM instead of 5-7PM

No take got the sign right: two read "7-5PM", one "5-5PM", one starts illegible. The model also flagged a blackboard that "magically teleports and appears from nowhere", an egg that morphs into a mini chalkboard mid-shot, and merged fingers on the grip. Each defect lands in the sidecar with its rule, timestamp, severity, and note, and each shows up as a color-coded dot in the batch's report. This is the yield problem in miniature: a mechanically clean batch, four takes deep, zero usable.

For contrast, here is what a good batch looks like: same pipeline, but a current model, Seedance 2, on a prompt inside its comfort zone, "a barista pours latte art into a white ceramic cup, steam rising, warm morning window light, slow push-in":

The contrast batch: Seedance 2, one prompt, four survivors. Ranked by flicker; take-003 first at 0.0009, take-001 last at 0.0018.
The exact prompt
Close-up: a barista pours latte art into a white ceramic cup, steam
rising, warm morning window light, shallow depth of field, slow
push-in, one continuous shot, no cuts

All four survive and rank, and the top of this batch's report is where your taste takes over. That pairing is the whole workflow: the tool clears the definitely-dead and the semantically broken, and you choose among takes worth choosing between.

What the funnel checks for

The full checklist, with what each check found in this tutorial's own batches. Stage 1 is always on; stage 2's rules are the built-in rubric, which you can extend or replace.

CheckCatchesFrom our batches
decode / black / freezeCorrupt files, black renders, stuck frames. The cheap deaths; kill on sight.Not triggered: hosted models rarely produce them. The kill line reads like frozen for 3.1s of 5.0s.
flicker + scene cutsLuminance instability, and a cut inside a single generated shot.Flicker ranked the latte takes (0.0009 best, 0.0018 worst).
anatomy.handsFinger counts, joints, grips."unnatural finger shape and merging" on the sign grip (bar, 2.333s)
anatomy.faces / anatomy.limbsWarped faces; extra or missing limbs, impossible poses.Not triggered in these batches; the crowd faces held up.
artifact.morphingObjects that morph, duplicate, or teleport."Blackboard magically teleports and appears from nowhere" (bar, 2.75s)
physics.contact / physics.motionHeld objects that slip, clip, or float; movement that ignores gravity."Egg instantly morphs into a blackboard sign with severe clipping through the metal utensil" (bar, 2.333s)
continuity.objectsProps that vanish or appear without leaving the frame."A coaster disappears from the bar top between 1.292 and 2.625" (beer slide); "The egg and spoon instantly vanish, replaced by a chalkboard sign" (bar, 3.875s)
environment.stabilityBackgrounds that melt or rearrange.Not triggered this round; the crowd banner's garble filed under text instead.
text.legibilityGarbled or unstable text anywhere in frame."HAPPY HOUR 7-5PM" for "5-7PM" (bar, 2.875s); "nonsensical AI-generated characters" on the banner (crowd, 0.0s)
adherence.promptDirection the model ignored. Needs the prompt in the sidecar."Balancing on one leg transitions into standing/walking posture" (juggler, 4.75s)

The other quotes come from three more small batches aimed at the categories the bar shot could not reach. The crowd batch (a wedding dance floor) stressed faces, motion, and backgrounds; one take died for the garbled banner, one passed clean, and the faces held up. The juggler batch (three oranges, one leg) stressed limbs and object permanence: one take passed, and one was killed for teleporting props, an orange trapped under a knee one frame and floating overhead the next. The beer-slide batch stressed liquid and object handoff; one take needs review for a hallucinated extra hand, and one was killed for merged fingers, a mug that changes shape between frames, and a coaster that vanishes off the bar. You do not get to pick which way a take fails, and you also do not get to force one: that is why the checks run as a battery, and why a clean pass is information too. Reports: crowd, juggler, beer slide.

The crowd, juggler, and beer-slide batches: MiniMax Hailuo 2.3. Watch the banner behind the dancers in the first take, and the mug in the last two.
The exact prompts
Crowd: A crowded dance floor at a wedding reception, many guests
dancing energetically, faces clearly visible in the foreground, a
couple spins past the camera, fast motion, string lights and a banner
in the background, one continuous shot

Juggler: A street performer juggles three oranges while balancing on
one leg on a plaza, audience faces watching close in the foreground,
fast hand motion, one continuous shot

Beer slide: A bartender slides a full beer mug down a wooden bar
counter to a customer who catches it, foam sloshing over the rim,
coasters and peanut bowls on the counter, one continuous shot

When something looks wrong

  • "error: ffmpeg not found on PATH": install ffmpeg (see Before you start) and open a new terminal.
  • "no clips found": check the path, and check the extensions; dailies looks for mp4, mov, webm, mkv, and avi.
  • "VLM endpoint unreachable": the URL should end in /v1, and hosted providers need DAILIES_VLM_KEY set in the same shell.
  • A take you fixed still shows the old verdict: reviews are cached by content hash; --force re-reviews everything.

Where this is going

Triage is half of the loop. The other half is memory: which seed, which model, which LoRA stack produced the take you kept. The take.json spec already reserves a recipe block for exactly that, and a companion tool that records it from a running ComfyUI is next. Together they close the loop the current workflow leaves open: generate, triage, keep, and reproduce the keeper next month on demand.

The code is on GitHub under MIT. Issues and rubric contributions are welcome, especially the rules you wrote for your own production.

Learn this beside the people building it.

Membership is free. Masterclasses from industry leaders, hackathons where you finish something the same day, and mentor circles matched to what you want to learn. For engineers and creatives alike, across film, design, image, sound, and story.