ffmpeg's select filter re-decodes the whole clip on every pass, so chunking by 30
turned a 670-frame job into 23 full decodes (minutes on shared CPU — long enough
to be killed mid-run and leave thumbnail_status stuck at 'running'). One pass now.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Deploy:
- Multi-stage Dockerfile (shadow-cljs release → Python+ffmpeg runtime), fly.toml
(one machine, scale-to-zero, SQLite on a volume), .dockerignore, DEPLOY.md
- Same-origin release build: api-port defaults "9001" for dev, "" in release
(no CORS in prod)
- env-driven settings (SECRET_KEY/DEBUG/ALLOWED_HOSTS/DB_PATH/CSRF), WhiteNoise
serving static + the SPA, R2 (S3-compatible) media storage when R2_* is set
- thumbnails reworked storage-agnostic: download clip, ffmpeg select-pass, upload
tiles via default_storage (R2 in prod, disk in dev)
- pin channels/daphne; add whitenoise/django-storages/boto3
UI:
- frame step buttons flanking play (disabled at timeline bounds)
- Add-annotation button moved into the toolbar, made prominent
- picker mode pulses a 1-bit glow on clips / frame readout / (while linking)
annotation bars; linking an annotation bar inserts a timeline link
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>