A complete end-to-end AI video generation and publishing system that transforms topics into YouTube-ready videos with research, scripting, scene planning, media generation, rendering, and YouTube publishing.
- ✅ Milestone 2: AI Brain (Research, Script, Scene Planning)
- ✅ Milestone 3A: Media Generation (Visuals, Audio, Subtitles)
- ✅ Milestone 3B: Video Rendering (FFmpeg)
- ✅ Milestone 4: YouTube Publishing
This milestone adds the AI pipeline for a YouTube video project: research, structured script generation, and scene planning. The system creates a typed project state and updates it through a status flow while keeping provider logic separate from the service layer.
- User/API
- Project Service
- AI Orchestrator
- Research Tool
- Script Generator
- Scene Planner
- VideoProject state
Create a local .env file based on .env.example:
LLM_API_KEY=your_api_key_here
LLM_BASE_URL=https://api.openai.com/v1
LLM_MODEL=gpt-4o-miniThe application reads the configured LLM credentials from environment variables and raises a clear configuration error if the key is missing.
Use the interface in app.providers.base and the provider implementation in app.providers.openai_provider. Provider-specific code remains isolated from the business logic.
Start the server:
python -m app.mainCreate a project:
curl -X POST http://127.0.0.1:8000/projects \
-H "Content-Type: application/json" \
-d '{
"topic": "5 unbelievable facts about space",
"duration_seconds": 60,
"audience": "general",
"style": "cinematic"
}'Response:
{
"project_id": "proj-1"
}Generate the AI pipeline:
curl -X POST http://127.0.0.1:8000/projects/proj-1/generateGet project state:
curl http://127.0.0.1:8000/projects/proj-1Create a project:
python -m app.cli.commands create --topic "5 unbelievable facts about space" --duration 60Generate the pipeline:
python -m app.cli.commands generate --id PROJECT_IDCheck status:
python -m app.cli.commands status --id PROJECT_IDLLM prompt definitions live in:
app/agent/prompts.py
Automatically publish rendered videos to YouTube using the YouTube Data API v3 with resumable upload support.
YouTube publishing is optional - the application boots normally without YouTube credentials. To enable YouTube publishing, add these environment variables to .env:
YOUTUBE_CLIENT_ID=your_client_id
YOUTUBE_CLIENT_SECRET=your_client_secret
YOUTUBE_REFRESH_TOKEN=your_refresh_token
YOUTUBE_DEFAULT_PRIVACY_STATUS=private # private, unlisted, or public
YOUTUBE_DEFAULT_CATEGORY_ID=28 # 28 = Science & TechnologyAlternatively, use a client secrets JSON file:
YOUTUBE_CLIENT_SECRETS_FILE=/path/to/client_secrets.jsonOnce a video is in VIDEO_READY state (after rendering), publish it to YouTube:
curl -X POST http://127.0.0.1:8000/projects/proj-1/publish \
-H "Content-Type: application/json" \
-d '{
"title": "5 Unbelievable Facts About Space",
"description": "Discover mind-bending facts about outer space...",
"tags": ["space", "astronomy", "facts"],
"privacy_status": "private"
}'Response:
{
"id": "proj-1",
"status": "UPLOADED",
"youtube_asset": {
"video_id": "abc123xyz",
"youtube_url": "https://www.youtube.com/watch?v=abc123xyz",
"privacy_status": "private",
"published_at": "2025-01-15T10:30:45.123456+00:00"
}
}Retrieve published video information:
curl http://127.0.0.1:8000/projects/proj-1/youtubeResponse:
{
"status": "UPLOADED",
"video_id": "abc123xyz",
"youtube_url": "https://www.youtube.com/watch?v=abc123xyz",
"privacy_status": "private",
"published_at": "2025-01-15T10:30:45.123456+00:00"
}CREATED → RESEARCHING → SCRIPT_GENERATED → SCENES_PLANNED
→ MEDIA_READY → VIDEO_READY → UPLOADING → UPLOADED
UPLOADING: Video is being uploaded to YouTubeUPLOADED: Successfully published to YouTubeFAILED: Upload failed; retry with working credentials
- ✅ Resumable chunked upload (handles network interruptions)
- ✅ Metadata override support (title, description, tags, category, privacy)
- ✅ State-based retry (failed uploads remain retryable)
- ✅ MP4 file preservation (video preserved if upload fails)
- ✅ No YouTube quota consumption during testing (MockPublisherProvider)
- ✅ Optional configuration (app boots without YouTube credentials)
LLM prompt definitions live in:
app/agent/prompts.py
Run the suite:
pytest- The generation pipeline is synchronous for Milestone 2 but structured so it can be converted to background jobs later.
- Validation is enforced with Pydantic models.
- Malformed LLM output triggers a controlled retry and then a clear failure state.
- The
/healthendpoint remains available and unchanged.