Jesse A. Green / notebook

MoneyPrinterTurbo Administrator & Developer Guide

Generated May 2026 from harry0703/MoneyPrinterTurbo at 46ba578

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

1. Architecture Overview

MoneyPrinterTurbo is an automated short-video generation platform that orchestrates LLM script writing, stock footage retrieval, text-to-speech synthesis, subtitle generation, and video compositing into a single pipeline. It exposes two user interfaces: a Streamlit-based WebUI for interactive use and a FastAPI REST API for programmatic integration.

The system is a monolithic Python application with clear internal layering: a configuration layer reads TOML settings, a service layer implements the core pipeline stages, a controller layer exposes HTTP endpoints, and a WebUI layer provides the graphical interface. State management is pluggable between in-memory storage and Redis.

Architecture Diagram

┌─────────────────────────────────────────────────────┐
│                    User / Client                     │
│         Browser (WebUI)  │  HTTP Client (API)        │
└────────────┬─────────────┴──────────┬────────────────┘
             │                        │
     ┌───────▼───────┐       ┌───────▼────────┐
     │ Streamlit App │       │ FastAPI (ASGI)  │
     │ webui/Main.py │       │  app/asgi.py    │
     └───────┬───────┘       │  app/router.py  │
             │               └───────┬────────┘
             │                       │
             └───────────┬───────────┘
                         │
              ┌──────────▼──────────┐
              │   Service Layer     │
              │  app/services/      │
              │  ├── task.py        │  ← Pipeline orchestrator
              │  ├── llm.py         │  ← LLM providers (14)
              │  ├── material.py    │  ← Stock footage search/download
              │  ├── voice.py       │  ← TTS engines (4)
              │  ├── subtitle.py    │  ← Transcription + SRT
              │  ├── video.py       │  ← MoviePy + ffmpeg compositing
              │  └── state.py       │  ← Memory or Redis state
              └──────────┬──────────┘
                         │
       ┌─────────┬───────┴───────┬──────────┐
       ▼         ▼               ▼          ▼
   External    Stock APIs     TTS APIs    ffmpeg
    LLMs       (Pexels,      (edge_tts,  (binary)
   (14+)       Pixabay)       Azure,
                              SiliconFlow,
                              Gemini)

Note

Both the Streamlit WebUI and the FastAPI API funnel into the same task.py:start() pipeline. There is no divergence in video generation logic between the two interfaces -- only the entry point differs.

Component Inventory

Component Path Role
FastAPI app app/asgi.py ASGI application, CORS middleware, static file mounts
Router app/router.py Aggregates v1 video + LLM routers
V1 Video API app/controllers/v1/video.py Task CRUD, BGM management, material uploads
V1 LLM API app/controllers/v1/llm.py Script and search-term generation endpoints
Health check app/controllers/ping.py GET /ping returns "pong"
Auth module app/controllers/base.py verify_token() checks X-API-Key header
Task manager app/controllers/manager/base_manager.py Thread-based concurrent task executor
Config loader app/config/config.py TOML load/save, environment variable overrides
LLM service app/services/llm.py 14 LLM providers, script/terms generation, 5-retry logic
Material service app/services/material.py Pexels/Pixabay search + download, TLS verification, API key rotation
Voice service app/services/voice.py 4 TTS backends, subtitle creation from word boundary data
Subtitle service app/services/subtitle.py faster-whisper transcription, SRT generation, Levenshtein correction
Video service app/services/video.py MoviePy compositing, ffmpeg concat, transitions, subtitle overlay
Task orchestrator app/services/task.py 7-step pipeline with progress tracking
State backends app/services/state.py MemoryState and RedisState implementations
Upload/Post app/services/upload_post.py Cross-post to TikTok/Instagram
Video effects app/services/utils/video_effects.py Fade/slide transitions
Utilities app/utils/utils.py Storage directory management, SRT formatting, JSON helpers, locale, threading
Schema models app/models/schema.py Pydantic models: VideoParams, VideoAspect, VideoConcatMode, etc.
Constants app/models/const.py PUNCTUATIONS, TASK_STATE_*, FILE_TYPE_* constants
Exceptions app/models/exception.py HttpException, FileNotFoundException
WebUI webui/Main.py Streamlit frontend (1,170 lines)
i18n webui/i18n/*.json 7 language translation files

Technology Stack

Layer Technology Version
Backend framework FastAPI + Uvicorn 0.115.6 / 0.32.1
Frontend Streamlit 1.45.0
Video processing MoviePy + ffmpeg 2.1.2
TTS (default) edge_tts 7.2.7
TTS (Azure V2) azure-cognitiveservices-speech 1.41.1
Speech-to-text faster-whisper 1.1.0
LLM (primary) OpenAI SDK 1.56.1
LLM (gateway) LiteLLM 1.60.0
LLM (Qwen) DashScope 1.20.14
LLM (Gemini) google.generativeai 0.8.6
LLM (free/risky) g4f 0.5.2.2 (optional)
State (optional) Redis 5.2.0
Config format TOML via toml library
Logging Loguru 0.7.3
Image processing Pillow via MoviePy

Class Hierarchy

BaseState(ABC)
  ├── MemoryState        # In-process dict-based state
  └── RedisState         # Redis-backed state

BaseResponse(BaseModel)
  ├── TaskResponse
  ├── TaskQueryResponse
  ├── TaskDeletionResponse
  ├── VideoScriptResponse
  ├── VideoTermsResponse
  ├── BgmRetrieveResponse
  ├── BgmUploadResponse
  ├── VideoMaterialRetrieveResponse
  └── VideoMaterialUploadResponse

TaskManager               # Thread-based concurrent task runner
UploadPostService         # Singleton for cross-posting
VideoParams(BaseModel)    # Core video parameter model

Enums:
  VideoConcatMode         # random, sequential
  VideoTransitionMode     # fade, slide, none
  VideoAspect             # portrait (9:16), landscape (16:9), square (1:1)

2. System Requirements

Hardware

Component Minimum Recommended
CPU 4 cores 8+ cores (video encoding is CPU-intensive)
RAM 4 GB 8+ GB (faster-whisper large-v3 model requires ~3 GB)
Disk 10 GB free 50+ GB (stock footage cache grows with usage)
GPU Not required NVIDIA GPU with CUDA (accelerates whisper and video encoding)

Software

Dependency Required Notes
Python 3.10+ 3.12 recommended
ffmpeg Required Must be on PATH or configured in config.toml
ImageMagick Required (Windows) Auto-detected on Linux/macOS; set imagemagick_path on Windows
Docker Optional For containerized deployment
NVIDIA drivers + CUDA Optional For GPU-accelerated whisper and video encoding
Redis Optional For multi-process state sharing

Network

Endpoint Purpose Required
LLM provider API Script generation At least one provider configured
Pexels API (api.pexels.com) Stock video search/download If video_source = "pexels"
Pixabay API (pixabay.com/api) Stock video search/download If video_source = "pixabay"
edge_tts (speech.platform.bing.com) Default TTS If using edge_tts
Azure Cognitive Services Azure TTS V2 If speech_key configured
SiliconFlow API SiliconFlow TTS If configured
Gemini API Gemini TTS / LLM If configured

Important

At minimum you need: (1) an LLM provider API key, (2) a Pexels or Pixabay API key, and (3) ffmpeg installed. Without all three, the pipeline cannot complete a video generation task.


3. Configuration Reference

Configuration is stored in config.toml at the project root. If the file does not exist on startup, it is copied from config.example.toml. The loader handles both UTF-8 and UTF-8-BOM encodings. The WebUI writes config changes on every "Generate Video" click and on page close.

Config sections are available as module-level dicts in code: config.app, config.whisper, config.proxy, config.azure, config.siliconflow, config.ui.

Important

All configuration keys below are sourced from config.example.toml -- the canonical ground truth. Do not invent configuration keys that are not listed here.

[app] -- Main Application Settings

Key Type Default Description
video_source string "pexels" Stock footage provider: "pexels" or "pixabay"
hide_config bool false Hide configuration panel in WebUI
edge_tts_timeout int 30 Timeout in seconds for edge_tts streaming; 0 disables timeout
tls_verify bool true Enable TLS certificate verification on outbound API requests
pexels_api_keys array [] Array of Pexels API keys (supports rotation)
pixabay_api_keys array [] Array of Pixabay API keys (supports rotation)
llm_provider string "openai" Active LLM provider (see provider list below)
enable_g4f bool false Enable g4f provider (disabled by default for security)
azure_api_version string "2024-02-15-preview" Azure OpenAI API version
subtitle_provider string "edge" Subtitle provider: "edge" (from TTS) or "whisper" (transcription)
imagemagick_path string "" Path to ImageMagick binary (Windows only; auto-detected on Linux/macOS)
ffmpeg_path string "" Path to ffmpeg binary (auto-detected if empty)
endpoint string "" Custom download URL base for API-generated videos
material_directory string "" Video cache directory. Default: ./storage/cache_videos. "task" = per-task directory. Or an absolute path.
enable_redis bool false Use Redis for state instead of in-memory
redis_host string "" Redis server hostname
redis_port int 6379 Redis server port
redis_db int 0 Redis database number
redis_password string "" Redis password
max_concurrent_tasks int 5 Maximum parallel video generation tasks
api_key string "" API key for verify_token() authentication
upload_post_enabled bool false Enable cross-posting to social platforms
upload_post_api_key string "" API key for Upload-Post service
upload_post_username string "" Username for Upload-Post service
upload_post_platforms array [] Target platforms (e.g., ["tiktok", "instagram"])
upload_post_auto_upload bool false Auto-upload after video generation

LLM Provider Configuration

Each provider uses the pattern {provider}_api_key, {provider}_base_url, {provider}_model_name:

Provider llm_provider value Required keys Notes
OpenAI "openai" openai_api_key, openai_model_name openai_base_url optional (defaults to OpenAI API)
Moonshot "moonshot" moonshot_api_key, moonshot_model_name, moonshot_base_url
Azure OpenAI "azure" azure_api_key, azure_model_name, azure_base_url Uses azure_api_version
Qwen (DashScope) "qwen" qwen_api_key, qwen_model_name Uses DashScope SDK
DeepSeek "deepseek" deepseek_api_key, deepseek_model_name, deepseek_base_url
ModelScope "modelscope" modelscope_api_key, modelscope_model_name, modelscope_base_url Streaming with enable_thinking=False
Gemini "gemini" gemini_api_key, gemini_model_name Auto-upgrades deprecated models
Ollama "ollama" ollama_model_name, ollama_base_url No API key needed (local)
g4f "g4f" enable_g4f = true Requires uv sync --extra g4f
OneAPI "oneapi" oneapi_api_key, oneapi_model_name, oneapi_base_url
Cloudflare "cloudflare" cloudflare_api_key, cloudflare_model_name, cloudflare_base_url
MiniMax "minimax" minimax_api_key, minimax_model_name, minimax_base_url
Ernie "ernie" ernie_api_key, ernie_model_name, ernie_base_url
Pollinations "pollinations" pollinations_model_name No API key required (public access)
LiteLLM "litellm" Varies by underlying provider Reads provider keys from environment variables

[whisper] -- Speech-to-Text Settings

Key Type Default Description
model_size string "large-v3" Whisper model size ("tiny", "base", "small", "medium", "large-v3")
device string "CPU" Compute device: "CPU" or "cuda"
compute_type string "int8" Quantization: "int8", "float16", "int8_float16"

Tip

On a machine with an NVIDIA GPU, set device = "cuda" and compute_type = "float16" for significantly faster whisper transcription. The large-v3 model with CUDA can be 10-20x faster than CPU.

[proxy] -- HTTP Proxy Settings

Key Type Default Description
http string "" HTTP proxy URL (e.g., "http://user:pass@proxy:1234")
https string "" HTTPS proxy URL

[azure] -- Azure Speech Services

Key Type Default Description
speech_key string "" Azure Cognitive Services speech key
speech_region string "" Azure region (e.g., "eastus")

[siliconflow] -- SiliconFlow TTS

Key Type Default Description
api_key string "" SiliconFlow API key

[ui] -- WebUI Display Settings

Key Type Default Description
hide_log bool false Hide log output in WebUI
subtitle_position string "bottom" Subtitle position on video
custom_position float 70.0 Custom subtitle Y-position (percentage from top)

Environment Variable Overrides

Variable Source Purpose
MPT_APP_REDIS_HOST app/config/config.py:70 Override Redis host (takes priority over REDIS_HOST)
REDIS_HOST app/config/config.py:72 Fallback Redis host override
CORS_ALLOWED_ORIGINS app/asgi.py:56 Comma-separated allowed CORS origins (default: "*")
IMAGEIO_FFMPEG_EXE app/services/video.py:63 Custom ffmpeg binary path
HF_ENDPOINT webui.sh Hugging Face mirror URL for model downloads
IMAGEMAGICK_BINARY app/config/config.py:137 Set automatically from imagemagick_path config

Config Loading Behavior

  1. On import, config.py attempts to load config.toml from the project root.
  2. If config.toml does not exist, it copies config.example.toml to config.toml.
  3. The loader tries UTF-8 first, then falls back to UTF-8-BOM encoding.
  4. Environment variables for Redis host (MPT_APP_REDIS_HOST or REDIS_HOST) override the TOML value at load time.
  5. When imagemagick_path is set in TOML, it is propagated to the IMAGEMAGICK_BINARY environment variable.
  6. The WebUI saves the entire config to disk on every "Generate Video" click and on Streamlit page close.

4. Deployment & Scaling

Local Development

# Clone and enter the project
git clone https://github.com/harry0703/MoneyPrinterTurbo.git
cd MoneyPrinterTurbo

# Install dependencies (uv recommended)
uv sync

# Optional: install g4f support
uv sync --extra g4f

# Copy config and edit
cp config.example.toml config.toml
# Edit config.toml with your API keys

# Start the API server
python main.py
# API available at http://localhost:8080
# Swagger docs at http://localhost:8080/docs

# Start the WebUI (separate terminal)
streamlit run webui/Main.py
# WebUI available at http://localhost:8501

Docker Deployment (Standard)

docker compose up -d

This starts the application in a container with the following bind mounts:

  • ./storage/ -- persistent storage for generated videos and cached materials
  • ./config.toml -- configuration file

Docker Deployment (GPU)

docker compose -f docker-compose.yml -f docker-compose.gpu.yml up -d

The GPU compose override adds NVIDIA runtime configuration, enabling CUDA-accelerated whisper transcription and video encoding.

Warning

Docker issue #885: The container runs as appuser, which may lack write permissions to bind-mounted storage/ directories. Ensure the storage/ directory and its subdirectories are writable by the container user before starting. Fix with: chmod -R a+w storage/ on the host.

Redis Setup (Multi-Process State)

For deployments running multiple worker processes or requiring state persistence across restarts:

# config.toml
[app]
enable_redis = true
redis_host = "localhost"
redis_port = 6379
redis_db = 0
redis_password = ""

In Docker, override the Redis host via environment variable:

# docker-compose.yml (excerpt)
environment:
  - MPT_APP_REDIS_HOST=redis

Note

Without Redis, state is held in a Python dict (MemoryState). This works for single-process deployments but means all task state is lost on restart. Redis provides persistence and cross-process visibility.

Scaling Considerations

Dimension Approach
Concurrent tasks Adjust max_concurrent_tasks in config (default: 5). Each task spawns a thread.
Multiple workers Enable Redis for shared state. Run multiple API instances behind a load balancer.
Disk I/O Stock footage cache (storage/cache_videos/) grows with usage. Set material_directory = "task" for per-task cleanup, or manage a shared cache with periodic pruning.
CPU bottleneck Video encoding (ffmpeg) is the primary bottleneck. Scale vertically with more cores or offload to GPU.
API key rotation Provide multiple keys in pexels_api_keys / pixabay_api_keys arrays; the material service rotates through them.

CORS Configuration

The FastAPI ASGI app configures CORS middleware. By default, all origins are allowed ("*"). To restrict:

export CORS_ALLOWED_ORIGINS="https://yourdomain.com,https://app.yourdomain.com"

5. Operations

Pipeline Lifecycle

The task.py:start() function executes a 7-step pipeline with progress tracking:

Step Function Progress Description
1 generate_script() 5% - 10% LLM generates video script from topic/prompt
2 generate_terms() 10% - 20% LLM generates stock footage search terms from script
3 generate_audio() 20% - 30% TTS engine converts script to audio file
4 generate_subtitle() 30% - 40% Generate SRT subtitles (from TTS word boundaries or whisper transcription)
5 get_video_materials() 40% - 50% Search and download stock footage clips
6 generate_final_videos() 50% - 100% Composite video: resize clips, overlay subtitles, mix audio, concat
7 cross_post_video() 100% Optional: upload to TikTok/Instagram

The stop_at parameter allows partial pipeline execution:

# Stop after script generation only
start(task_id, params, stop_at="script")

# Stop after audio generation
start(task_id, params, stop_at="audio")

# Full pipeline (default)
start(task_id, params, stop_at="video")

Valid stop_at values: "script", "terms", "audio", "subtitle", "materials", "video".

Task State Machine

pending ──► processing ──► completed
                │
                └──► failed

State transitions are managed by TaskManager (thread-based). Tasks are tracked by task_id in the configured state backend (MemoryState or RedisState). The state.update_task() method updates progress percentage and status atomically.

Warning

With MemoryState (the default), all task state is lost when the process restarts. There is no automatic retry mechanism for interrupted tasks. If you need task persistence, enable Redis.

Storage Layout

storage/
├── tasks/
│   └── {task_id}/
│       ├── audio.mp3          # Generated TTS audio
│       ├── subtitle.srt       # Generated subtitles
│       ├── script.json        # Generated script
│       ├── terms.json         # Search terms
│       └── final-*.mp4        # Output video(s)
├── cache_videos/              # Downloaded stock footage cache
├── bgm/                       # Background music files
└── fonts/                     # Custom fonts

Tip

The storage/tasks/ directory serves double duty: it is both the working directory during generation and the serving directory for the API. The FastAPI app mounts storage/tasks/ at /tasks/{task_id}/ for direct file access.

Monitoring

The health check endpoint provides basic liveness:

curl http://localhost:8080/ping
# Response: "pong"

For deeper monitoring, query the task list endpoint:

curl http://localhost:8080/api/v1/videos?page=1&page_size=20

Loguru handles all application logging. Log output goes to stdout by default and can be observed in the WebUI (unless ui.hide_log = true).

Backup and Recovery

Critical data paths requiring backup:

Path Content Priority
config.toml All configuration including API keys High
storage/tasks/ Generated videos and intermediate files Medium
storage/cache_videos/ Downloaded stock footage (re-downloadable) Low
storage/bgm/ Uploaded background music Medium

6. Performance

LLM Service Performance

  • Retry logic: 5 retries per LLM request with exponential backoff behavior.
  • Response normalization: every LLM response is stripped of whitespace, newlines removed, and validated as non-empty before proceeding.
  • Gemini model auto-upgrade: deprecated model names (gemini-pro, gemini-1.0-pro) are automatically mapped to gemini-2.5-flash to prevent API failures.
  • ModelScope streaming: uses streaming mode with enable_thinking=False to avoid unnecessary reasoning token overhead.

Video Processing Performance

  • ffmpeg binary resolution order: IMAGEIO_FFMPEG_EXE env var, system PATH, imageio_ffmpeg bundled binary, then bare "ffmpeg" fallback.
  • Concat strategy: individual clips are written as temp files, then joined using ffmpeg's concat demuxer with a single re-encode pass. This avoids repeated decode/encode cycles.
  • Clip looping: if total video duration is shorter than the audio track, clips are looped cyclically to fill the gap.
  • Resize strategy: aspect ratio is maintained; black letterboxing fills any remaining space.
  • Image sanitization: corrupt EXIF/metadata is stripped from images before compositing to prevent MoviePy crashes.
  • Audio output: AAC codec at 192 kbps bitrate.
  • Stdout suppression: MoviePy's verbose ffmpeg probe output is redirected to avoid log pollution.

Voice Service Performance

  • edge_tts: async streaming with configurable timeout (edge_tts_timeout, default 30 seconds). Set to 0 to disable timeout for slow connections.
  • Azure TTS V2: word boundary callbacks provide precise subtitle timing without a separate transcription pass.
  • Subtitle creation: SubMaker uses word boundary data from TTS for alignment. A legacy compatibility layer (ensure_legacy_submaker_fields()) handles older edge_tts versions.

Material Download Performance

  • API key rotation: when multiple Pexels/Pixabay keys are provided, the service rotates through them to distribute rate limits.
  • TLS verification: configurable via tls_verify. Disabling is not recommended but may be necessary behind corporate proxies.
  • Caching: downloaded footage is stored in storage/cache_videos/ (or per-task if material_directory = "task"). Subsequent requests for the same footage hit the cache.

7. Security

Caution

API authentication (verify_token()) exists in the codebase at app/controllers/base.py but is currently commented out in all routers (GitHub issue #882). Any deployment exposed to the network is effectively unauthenticated. Re-enable auth in production by uncommenting the Depends(verify_token) declarations in app/controllers/v1/video.py and app/controllers/v1/llm.py.

Authentication

The verify_token() function in app/controllers/base.py checks the X-API-Key header against config.app.api_key. When enabled:

# In router files, add dependency:
@router.post("/api/v1/videos", dependencies=[Depends(verify_token)])

To set the API key:

# config.toml
[app]
api_key = "your-strong-random-key-here"

TLS Verification

GitHub issue #881 identified that TLS certificate verification was previously disabled on all outbound API requests. This is now configurable:

[app]
tls_verify = true   # Default: true. Set to false only if behind a corporate proxy with MITM cert.

Warning

Setting tls_verify = false disables certificate validation for all outbound HTTP requests (stock footage downloads, LLM API calls). This makes the application vulnerable to man-in-the-middle attacks. Only disable in trusted network environments.

g4f Supply Chain Risk

The g4f package (GitHub issue #883) relies on reverse-engineered third-party endpoints and has been flagged as a supply chain risk. It is disabled by default and requires explicit opt-in:

[app]
enable_g4f = true   # Not recommended for production

Additionally, g4f is not installed with default dependencies. It requires:

uv sync --extra g4f

Caution

The g4f package executes code from reverse-engineered third-party services. It may send data to uncontrolled endpoints, introduce vulnerabilities, or break without notice. Avoid in production environments or any deployment handling sensitive content.

CORS

Default CORS configuration allows all origins ("*"). For production:

export CORS_ALLOWED_ORIGINS="https://yourdomain.com"

API Key Storage

API keys for LLM providers and stock footage services are stored in plaintext in config.toml. Protect this file:

chmod 600 config.toml

In Docker deployments, mount config.toml as a read-only secret or use environment variable overrides where available.

Static File Serving

The FastAPI app serves generated videos directly from storage/tasks/{task_id}/. Without authentication enabled, any user with a task ID can access any generated video. Task IDs are sequential or predictable depending on the state backend implementation.


8. Developer Internals

LLM Service (app/services/llm.py)

The LLM service is the most complex service module, supporting 14 providers through a unified interface.

Request flow:

  1. Caller invokes generate_script(topic, language) or generate_terms(topic, script, amount).
  2. The service selects the configured provider and constructs a provider-specific API call.
  3. Each call is wrapped in a retry loop (max 5 attempts).
  4. The response is normalized: whitespace stripped, newlines removed, validated as non-empty.
  5. If all retries fail, the service raises an exception.

Provider-specific behaviors:

Provider Implementation Detail
OpenAI-compatible Uses OpenAI SDK ChatCompletion.create() with configurable base_url
Gemini Uses google.generativeai SDK; auto-upgrades deprecated model names
Qwen Uses DashScope SDK with Qwen-specific parameters
ModelScope Streaming mode with enable_thinking=False
Ollama OpenAI-compatible SDK pointed at local Ollama server
g4f Direct g4f.ChatCompletion.create(), requires explicit enable flag
Pollinations No API key needed; public endpoint
LiteLLM Delegates to LiteLLM gateway, which reads provider keys from environment

Video Service (app/services/video.py)

ffmpeg resolution chain:

# Priority order:
1. os.environ["IMAGEIO_FFMPEG_EXE"]
2. shutil.which("ffmpeg")        # System PATH
3. imageio_ffmpeg.get_ffmpeg_exe()  # Bundled
4. "ffmpeg"                      # Bare fallback

Video assembly process:

  1. Clips are loaded and resized to target aspect ratio (9:16, 16:9, or 1:1).
  2. If total clip duration < audio duration, clips are looped cyclically.
  3. Transitions (fade, slide, or none) are applied between clips.
  4. Subtitles are rendered as text overlays on the video frames.
  5. Individual processed clips are written as temp files.
  6. All temp clips are concatenated using ffmpeg's concat demuxer.
  7. Audio track is mixed with optional background music.
  8. Final output is written as MP4 with AAC audio at 192 kbps.

Error handling:

If the ffmpeg concat step fails, a RuntimeError: ffmpeg concat failed is raised. This typically indicates a codec mismatch between clips or a corrupt temp file.

Voice Service (app/services/voice.py)

Four TTS backends:

  1. edge_tts (V1): Uses edge_tts.Communicate with async streaming. Word boundary data from the stream is used to generate subtitle timing. Configurable timeout prevents hangs on network issues.

  2. Azure TTS V2: Uses the azure-cognitiveservices-speech SDK with word boundary event callbacks. Provides the most accurate subtitle timing of all backends.

  3. SiliconFlow: REST API calls to siliconflow.cn. Returns audio as a single blob (no word boundary data; subtitles require separate whisper transcription).

  4. Gemini TTS: Uses google.generativeai SDK for TTS. Returns audio without word boundary data.

Subtitle Service (app/services/subtitle.py)

Two modes of subtitle generation:

  • edge mode (subtitle_provider = "edge"): Subtitles are generated from TTS word boundary data during audio synthesis. Fast and accurate for edge_tts and Azure backends.
  • whisper mode (subtitle_provider = "whisper"): Uses faster-whisper to transcribe the generated audio. Required when the TTS backend does not provide word boundary data (SiliconFlow, Gemini). Levenshtein distance correction aligns transcribed text with the original script to fix minor transcription errors.

State Service (app/services/state.py)

Two backends implementing the BaseState abstract class:

  • MemoryState: Python dict wrapped in the BaseState interface. Zero dependencies, instant access, but state is process-local and non-persistent.
  • RedisState: Serializes task state to Redis. Supports multi-process deployments and survives restarts.

Selection is controlled by config.app.enable_redis. The state object is instantiated once at module level and used throughout the application.

Task Manager (app/controllers/manager/base_manager.py)

The TaskManager uses Python's threading module to run video generation tasks concurrently. Tasks are submitted as threads, up to max_concurrent_tasks (default 5). Each thread runs the task.py:start() pipeline independently.

Upload-Post Service (app/services/upload_post.py)

Optional cross-posting to social media platforms (TikTok, Instagram). Implemented as a singleton UploadPostService. Requires:

  • upload_post_enabled = true
  • upload_post_api_key set
  • upload_post_username set
  • upload_post_platforms configured

9. Integration Points

REST API Reference

The API runs at http://host:8080 with interactive Swagger documentation at /docs.

Video Task Endpoints

Method Path Description
POST /api/v1/videos Create a video generation task
GET /api/v1/videos List tasks (query params: page, page_size)
GET /api/v1/videos/{task_id} Get task status and progress
DELETE /api/v1/videos/{task_id} Delete/cancel a task

Generation Sub-Endpoints

Method Path Description
POST /api/v1/videos/script Generate script only (body: VideoScriptRequest)
POST /api/v1/videos/terms Generate search terms (body: VideoTermsRequest)
POST /api/v1/videos/subtitle Generate audio + subtitles
POST /api/v1/videos/audio Generate audio only

Resource Management

Method Path Description
GET /api/v1/videos/bgm List available background music files
POST /api/v1/videos/bgm/upload Upload a background music file
GET /api/v1/videos/materials List available material files
POST /api/v1/videos/materials/upload Upload material files

Static File Access

Pattern Description
/tasks/{task_id}/... Serves generated files from storage/tasks/
/ Serves resource/public/index.html

Example: Create a Video Task

curl -X POST http://localhost:8080/api/v1/videos \
  -H "Content-Type: application/json" \
  -d '{
    "video_subject": "The Future of Renewable Energy",
    "video_language": "en-US",
    "video_aspect": "portrait",
    "video_concat_mode": "random",
    "video_transition_mode": "fade",
    "paragraph_number": 3,
    "voice_name": "en-US-AriaNeural",
    "bgm_type": "random",
    "subtitle_enabled": true,
    "font_name": "STHeitiMedium.ttc",
    "text_fore_color": "#FFFFFF",
    "font_size": 60,
    "stroke_color": "#000000",
    "stroke_width": 1.5
  }'

Example: Poll Task Status

curl http://localhost:8080/api/v1/videos/{task_id}

Response includes progress (0-100) and state (pending, processing, completed, failed).

WebUI Integration

The Streamlit WebUI (webui/Main.py, 1,170 lines) provides:

  • Topic/prompt input with language selection (7 languages via i18n)
  • Full configuration panel (mirrors config.toml settings)
  • Real-time progress display during generation
  • Video preview and download
  • Configuration auto-save on generate and page close

The WebUI communicates with the backend by directly importing and calling service-layer functions -- it does not go through the REST API.

Internationalization

Seven language files in webui/i18n/:

webui/i18n/
├── en.json
├── zh.json
├── ja.json
├── ko.json
├── fr.json
├── de.json
└── es.json

External Service Dependencies

Service Required Configuration
LLM Provider Yes (one minimum) [app] section, provider-specific keys
Pexels If video_source = "pexels" pexels_api_keys array
Pixabay If video_source = "pixabay" pixabay_api_keys array
Azure Speech Optional TTS [azure] section
SiliconFlow Optional TTS [siliconflow] section
Gemini Optional TTS/LLM gemini_api_key in [app]
Redis Optional state backend [app] Redis settings
Hugging Face Whisper model download HF_ENDPOINT env var for mirror
Upload-Post API Optional cross-posting [app] upload_post settings

Troubleshooting

Error: "{provider}: api_key is not set, please set it in the config.toml file."

Cause: The selected LLM provider does not have an API key configured.

Solution: Edit config.toml and set the {provider}_api_key value for your chosen provider. Restart the application.

Prevention: Verify all required provider keys are set before starting.


Error: "{provider}: model_name is not set, please set it in the config.toml file."

Cause: The LLM provider is configured but no model name is specified.

Solution: Set {provider}_model_name in config.toml. For example: openai_model_name = "gpt-4o".


Error: "{provider}: base_url is not set, please set it in the config.toml file."

Cause: Providers that require a custom endpoint (Moonshot, Azure, DeepSeek, ModelScope, OneAPI, Cloudflare, MiniMax, Ernie) need {provider}_base_url set.

Solution: Set the appropriate {provider}_base_url in config.toml.


Error: "[{provider}] returned an empty response"

Cause: The LLM returned a blank response after all 5 retry attempts. This may indicate rate limiting, an overloaded model, or a prompt issue.

Solution: Check the LLM provider's status page. Try a different model. Verify your API key has sufficient quota. Check network connectivity and proxy settings.


Error: "[{provider}] returned an invalid response"

Cause: The LLM response could not be parsed as expected text. May indicate a model returning structured data when plain text was expected.

Solution: Verify the model name is correct. Some models return JSON by default; ensure you are using a chat-completion-compatible model.


Error: "g4f provider is disabled by default because it relies on reverse-engineered third-party endpoints"

Cause: Attempting to use the g4f provider without explicitly enabling it.

Solution: Set enable_g4f = true in config.toml if you accept the security risks. This is not recommended for production.


Error: "g4f package is not installed by default. Install the optional dependency with uv sync --extra g4f"

Cause: g4f is enabled in config but the Python package is not installed.

Solution: Run uv sync --extra g4f to install the g4f dependency.


Error: "RuntimeError: ffmpeg concat failed"

Cause: The ffmpeg concat demuxer could not join the processed video clips. Common causes: codec mismatch between clips, corrupt temp file, insufficient disk space, or ffmpeg not found.

Solution:

  1. Verify ffmpeg is installed and accessible: ffmpeg -version.
  2. Check available disk space in the storage/ directory.
  3. Inspect individual temp clips in the task directory for corruption.
  4. If using Docker, ensure the ffmpeg binary is available inside the container.

Prevention: Set ffmpeg_path explicitly in config.toml if auto-detection is unreliable.


Error: "invalid voice name: {voice_name}"

Cause: The specified TTS voice name does not match any available voice in the selected TTS engine.

Solution: Use a valid voice name. For edge_tts, run edge-tts --list-voices to see available voices. Common voices: en-US-AriaNeural, en-US-GuyNeural, zh-CN-XiaoxiaoNeural.


Error: "edge_tts stream timed out after {N}s"

Cause: The edge_tts service did not respond within the configured timeout (edge_tts_timeout).

Solution:

  1. Check network connectivity to Microsoft's TTS servers.
  2. Increase edge_tts_timeout in config.toml (default: 30 seconds).
  3. Set edge_tts_timeout = 0 to disable the timeout entirely (not recommended for production).
  4. If behind a proxy, configure [proxy] settings.

Error: "edge_tts communicate object has no stream method"

Cause: Incompatible edge_tts version. The stream() method interface changed between versions.

Solution: Update edge_tts to version 7.2.7 or later: uv sync. The voice service includes a legacy compatibility layer (ensure_legacy_submaker_fields()), but severely outdated versions may not be supported.


Error: "video_terms must be a string or a list of strings"

Cause: The video terms parameter was passed in an unexpected format (e.g., a dict or nested structure).

Solution: Ensure video_terms is either a single string or a flat list of strings when calling the API.


Problem: Docker container cannot write to storage directory

Cause: GitHub issue #885. The Docker container runs as appuser (non-root), which may lack write permissions to bind-mounted host directories.

Solution:

chmod -R a+w storage/
# Or set ownership to match container user:
chown -R 1000:1000 storage/

Problem: Videos generate but are not accessible via API

Cause: The FastAPI static file mount expects files at storage/tasks/{task_id}/. If material_directory or storage paths are misconfigured, files may be written elsewhere.

Solution: Verify that generated files exist in storage/tasks/{task_id}/ and that the directory is mounted correctly in Docker. Check the endpoint config if using a custom download URL.


Problem: Whisper transcription is extremely slow

Cause: Whisper is running on CPU with the large-v3 model (default), which is very slow without GPU acceleration.

Solution: Either:

  1. Switch to GPU: set whisper.device = "cuda" and whisper.compute_type = "float16".
  2. Use a smaller model: set whisper.model_size = "small" or "base" (reduced accuracy).
  3. Use edge subtitle provider instead: set subtitle_provider = "edge" to skip whisper entirely.

Problem: API requests return CORS errors in browser

Cause: Default CORS allows all origins ("*"), but if CORS_ALLOWED_ORIGINS is set incorrectly, requests from your frontend domain will be blocked.

Solution: Set CORS_ALLOWED_ORIGINS to include your frontend's origin, comma-separated:

export CORS_ALLOWED_ORIGINS="http://localhost:3000,https://yourdomain.com"

Problem: Generated videos have black bars

Cause: Source footage aspect ratio does not match the target video_aspect setting. The resize strategy maintains aspect ratio and fills remaining space with black letterboxing.

Solution: This is expected behavior. Choose footage that matches your target aspect ratio, or accept the letterboxing. The system does not crop or stretch video to fill.


Testing

Tests use Python's unittest framework:

python -m unittest discover -s test

Test files in test/services/:

File Coverage
test_llm.py LiteLLM provider integration
test_material.py TLS verification behavior
test_task.py Task service pipeline
test_video.py Video preprocessing and assembly
test_voice.py Voice synthesis

See the User Guide for basic usage and video generation workflows.