Backend (Phase A):
- A1: Adaptive silence buffer — natural_gap_ms persisted per cue; renderer computes
per-cue silence_before/silence_after instead of fixed 500ms; per-cue silence files
- A2: Forward-preferred snap — snap_pause_point prefers boundaries up to 4s ahead
over boundaries within 1.5s behind, reducing mid-scene cuts
- A3: Min-gap validation — pause points with < 200ms gap trigger forward search
to the next acceptable gap
- natural_gap_ms added to PausePointData model and api.ts type
- New config fields: whisper_snap_forward_window, whisper_snap_backward_window,
ad_silence_buffer_default, ad_silence_buffer_min_after, ad_min_acceptable_gap
- Tests: test_whisper_snap.py (13 tests), test_video_renderer_buffers.py
Frontend (Phase B):
- B1: Drag pause-point markers — pointer state machine with 3px move threshold,
clamp to min/max bounds, click-without-move still opens PausePointEditor
- B2: Drag freeze blocks — orange blocks translate with linked pause point
- B3: Time tooltip visible during drag, hidden on release
- Tests: TimelinePreview.drag.test.tsx (10 tests)
Fixes:
- Share link pointed to ai-sandbox.oliver.solutions — added app_url to Settings
with correct optical-dev.oliver.solutions default; share_url now configurable
via APP_URL env var
- Removed all ai-sandbox.oliver.solutions references from docker-compose,
apache config, docs, and scripts
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Hardcoded --concurrency=8 with 512MB memory limit caused 1162+ OOM restarts.
Default is 2; set TTS_WORKER_CONCURRENCY in .env.production to override.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- docker-compose.yml: add USE_CELERY_FALLBACK env var to api and worker
services so cloud_run_dispatch uses Celery on optical-dev
- JobDetail.tsx: show actual error message instead of generic
"Processing failed at ." when failure step is unknown; also show
job.error string when no structured failure object exists
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Port 8003 is occupied by infra-api-1 on optical-dev server.
Artifact Registry repo renamed from nexus to video-accessibility.
cloudbuild.yaml defaults _TAG to 'latest' for manual runs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New services/cost_tracker.py: sync httpx preflight()/record() + async wrappers;
BudgetExceeded exception; no-op when COST_TRACKER_BASE_URL is empty
- Preflight budget check added before ingestion (Gemini), per-language translation
(video-native + traditional), and per-language TTS dispatch
- _record_gemini_usage and _record_tts_cost now call cost_tracker directly;
removes broken asyncio.get_event_loop() hack from sync Celery worker
- Fix: _cost_ctx now threaded into extract_accessibility_targeted (video-native path)
- Fix: user_id/cost_project_id now propagated through dispatch_language_tts →
synthesize_cue_task.s() and the rerender_accessible_video.py re-render path
- Remove oliver-cost-tracker SDK dependency (was commented-out/never installed)
- Drop cost_tracker_outbox_path setting and get_cost_tracker() factory
- Update COST_TRACKER_BASE_URL default to optical-dev.oliver.solutions in
.env.prod.example, docker-compose.yml, and all Cloud Run service yamls
- Cloud Run yamls use Secret Manager ref (cost-tracker-api-key) for the API key
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Allow configuring Celery worker concurrency via environment variables
to take advantage of Cloud Run autoscaling:
- Add WORKER_CONCURRENCY, WHISPER_WORKER_CONCURRENCY, FFMPEG_WORKER_CONCURRENCY
settings to config.py with recommended values documented
- Update Dockerfile to use ${WORKER_CONCURRENCY} and ${WHISPER_WORKER_CONCURRENCY}
environment variables instead of hardcoded values
- Update docker-compose.yml to pass concurrency env vars to worker commands
- Add WHISPER_SERVICE_URL and FFMPEG_SERVICE_URL to relevant workers
Recommended settings:
Local mode: WHISPER=1, FFMPEG=1 (CPU/RAM constrained)
Cloud Run mode: WHISPER=10, FFMPEG=20 (match autoscaling limits)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Break out TTS synthesis into a dedicated Celery worker (tts queue) with
concurrency=8 for parallel processing. Each AD cue is now synthesized as
a separate task, enabling up to 8 cues to be processed simultaneously.
Key changes:
- Add tts_synthesis.py with synthesize_cue_task for per-cue synthesis
- Refactor translate_and_synthesize.py to dispatch cue tasks in parallel
- Add tts-worker service to docker-compose.yml (concurrency=8)
- Add Cloud Run service config for production deployment
Benefits:
- Parallel synthesis even for single jobs (e.g., 50 cues → 8 concurrent)
- Natural rate limiting across multiple concurrent jobs
- Fault tolerance with per-cue retries and GCS persistence
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Medium model is faster and uses less memory (~1.5GB vs ~3GB)
while still providing good multilingual transcription quality.
Updated in:
- config.py
- docker-compose.yml
- whisper-worker-service.yaml
- cloudbuild.yaml
- Dockerfile (pre-download)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Environment variables were overriding config.py with 'base' model.
Updated defaults in:
- docker-compose.yml
- whisper-worker-service.yaml
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implements word-level speech analysis using faster-whisper to refine
AD pause points. Gemini's timestamps are snapped to natural speech gaps
(sentence/phrase boundaries) to prevent pauses mid-word.
Key changes:
- Add WhisperService for transcription and gap detection
- Add dedicated Celery task routed to 'whisper' queue
- Integrate refinement into render_accessible_video task
- Cache Whisper transcripts in MongoDB for reuse across languages
- Add dedicated whisper-worker with concurrency=1 to prevent OOM
Configuration:
- Uses faster-whisper 'base' model (multilingual, ~145MB)
- 5-second search window after Gemini's recommended point
- Falls back to original timestamp if no gap found
Infrastructure:
- New Docker stage: whisper-worker
- New Cloud Run service: accessible-video-whisper-worker
- Updated docker-compose.yml with whisper-worker service
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Modified render_accessible_video.py to explicitly pass TMPDIR to
tempfile.TemporaryDirectory() so files are created in shared volume
- Updated docker-compose.yml to run containers as root initially,
chown /shared-tmp to app:app, then switch to app user for celery
- This ensures both worker containers can access the same temp files
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The main worker and ffmpeg-worker run in separate containers with
isolated filesystems. Add a shared volume mounted at /shared-tmp
and set TMPDIR so Python's tempfile module uses it. This allows
ffmpeg-worker to access video files created by the main worker.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add a dedicated Celery queue (ffmpeg) with concurrency=1 to serialize
all FFmpeg operations. This prevents CPU spikes when multiple render
tasks run in parallel with multiple languages.
Changes:
- Add ffmpeg_operations.py with run_ffmpeg_command and run_ffprobe_command tasks
- Update VideoRendererService to dispatch ffmpeg commands via the queue
- Add ffmpeg-worker service to docker-compose with --concurrency=1
- Configure main worker to exclude the ffmpeg queue
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add all logging components with verbosity 0 in mongod.conf
- Add WiredTiger verbose=[] to suppress checkpoint messages
- Add --quiet flag to mongod command
- Change healthcheck from mongosh to TCP port check to avoid
connection metadata spam every 30s
- Increase healthcheck interval from 30s to 60s
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>