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.
pip install video-dailies, thendailies review ./takes. Dead takes get killed with reasons; survivors get ranked per shot.dailies reportwrites one static HTML page: hover to scrub, defects marked on a timeline.dailies watchreviews 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 ffmpegon a Mac,sudo apt install ffmpegon 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 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 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.
| Check | Catches | From our batches |
|---|---|---|
| decode / black / freeze | Corrupt 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 cuts | Luminance instability, and a cut inside a single generated shot. | Flicker ranked the latte takes (0.0009 best, 0.0018 worst). |
| anatomy.hands | Finger counts, joints, grips. | "unnatural finger shape and merging" on the sign grip (bar, 2.333s) |
| anatomy.faces / anatomy.limbs | Warped faces; extra or missing limbs, impossible poses. | Not triggered in these batches; the crowd faces held up. |
| artifact.morphing | Objects that morph, duplicate, or teleport. | "Blackboard magically teleports and appears from nowhere" (bar, 2.75s) |
| physics.contact / physics.motion | Held 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.objects | Props 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.stability | Backgrounds that melt or rearrange. | Not triggered this round; the crowd banner's garble filed under text instead. |
| text.legibility | Garbled 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.prompt | Direction 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 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 needDAILIES_VLM_KEYset in the same shell. - A take you fixed still shows the old verdict: reviews are cached by content hash;
--forcere-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.