4.3 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
What is VideoRoll
VideoRoll is a modular video processing pipeline: YouTube ingest → ASR subtitles → translation → rendering (burn-in) → Bilibili publishing. It consists of a Python/FastAPI backend, Celery workers, and a React/TypeScript frontend.
Commands
Development environment
# Start all services (docker compose with Intel GPU passthrough)
./scripts/dev_up.sh
# Stop services
./scripts/dev_down.sh
# Health check all endpoints
./scripts/dev_health.sh
# Tail logs
./scripts/dev_logs.sh
# Frontend dev server (Vite, port 3000)
./scripts/dev_web.sh
Testing
python -m pytest tests/ # all tests
python -m pytest tests/test_publish_meta_draft.py # single file
Tests use unittest.TestCase style. No conftest.py or shared fixtures.
Frontend
cd src/web
npm run lint # ESLint
npm run build # Vite production build
Architecture
Monolith assembly
All four FastAPI apps run in a single uvicorn process (videoroll.apps.monolith.main:app):
- Orchestrator (
/api) — task CRUD, asset management, auth, YouTube download, publish meta, settings - Subtitle Service (
/api/subtitle-service) — ASR, translation, render queue, auto profile - YouTube Ingest (
/api/youtube-ingest) — source CRUD, channel/playlist scanning - Bilibili Publisher (
/api/bilibili-publisher) — auth, publish jobs, typeid recommendation
The entrypoint (docker/entrypoint.sh) runs uvicorn + 2 Celery workers in parallel (subtitle queue, publish queue).
Task state machine
CREATED → INGESTED → DOWNLOADED → AUDIO_EXTRACTED → ASR_DONE → TRANSLATED → SUBTITLE_READY → RENDERED → READY_FOR_REVIEW → APPROVED → PUBLISHING → PUBLISHED (+ FAILED, CANCELED)
Services communicate only through DB task state + S3 storage keys — no direct data passing.
Celery workers
| App | Queue | Key tasks |
|---|---|---|
videoroll.apps.subtitle_service.worker |
subtitle |
task_queue_tick (scheduler), process_job (ASR+translate), process_render_job (ffmpeg burn-in), auto_youtube_pipeline, after_render_publish, cleanup_task |
videoroll.apps.bilibili_publisher.worker |
publish |
process_job (upload to Bilibili) |
The subtitle queue uses a task-level lock system (Task.lock_owner / Task.lock_until) with configurable max_concurrency. task_queue_tick is the central scheduler that picks queued work and dispatches within concurrency limits.
Database
PostgreSQL with psycopg 3 driver. SQLAlchemy 2 ORM (DeclarativeBase + Mapped annotations). No Alembic — uses a lightweight auto-migration system (db/auto_migrate.py) that runs ALTER TABLE ADD COLUMN on startup.
Configuration
videoroll/config.py — pydantic-settings, one Settings class per service inheriting from CommonSettings. All env-driven with .env file support, cached via @lru_cache.
Key shared modules
| Path | Purpose |
|---|---|
videoroll/db/models.py |
All 11 ORM models (Task, Asset, Subtitle, PublishJob, etc.) |
videoroll/storage/s3.py |
boto3 S3/MinIO wrapper |
videoroll/ai/ |
OpenAI client (translation, typeid recommendation, content review) |
videoroll/utils/crypto.py |
Fernet encryption for secrets at rest |
videoroll/apps/publish_meta_rules.py |
Pure functions for Bilibili-compliant metadata (CJK-aware text clamping) |
videoroll/apps/publish_meta_draft.py |
Builds publish meta drafts from YouTube metadata + settings |
Frontend
React 18 + TypeScript + Vite + Tailwind CSS + react-router-dom v6. In production, nginx serves the SPA and reverse-proxies /api/ to the backend. In dev, Vite proxies /api to localhost:8000.
External services (from compose)
- Redis — Celery broker/backend
- MinIO — S3-compatible object storage
- PostgreSQL 16+ — provided externally (not in compose)
Conventions
- Python >=3.12, build with hatchling
- Source layout:
src/videoroll/ - ASR engine is pluggable:
faster-whisper(default),openvino(Intel GPU), ormock(testing) - YouTube integration via
yt-dlpwith configurable proxy, cookies (Netscape format), user-agent - Sensitive data (API keys, cookies) encrypted at rest with Fernet (
data/secrets/fernet.key)