CodeVetter · Repo Unpack · Contributor handoff
harry0703/MoneyPrinterTurbo
Start at app/services/task.py (_run_pipeline is the shared spine), app/models/schema.py (VideoParams), and app/config/config.py (global config + locking). All three frontends must stay behaviorally identical — comments throughout document past drift bugs where a value list or check existed in only one entrypoint. Tests run via uv + pytest with a 70% branch-coverage floor; ruff lints the same file set as coverage. Preserve the security invariants: path confinement, optional auth, no secrets in logs, and never resurrecting deleted task records.
- Read first: app/services/task.py (pipeline + failure semantics), app/models/schema.py (VideoParams and request models), app/config/config.py (config load/save/locks), cli.py's top constant block, and the relevant section of webui/Main.py before editing any pipeline-adjacent code.
- Test commands: `uv sync --frozen` then `uv run python -X utf8 -m pytest -q test`; coverage parity with CI is `coverage run -m pytest -q test` + `coverage report` (fail_under=70, branch mode); lint with `ruff check app cli.py main.py webui test docs/skill`.
- Keep value lists synchronized across cli.py constants (_CLI_VIDEO_SOURCES, _SUBTITLE_POSITION_VALUES, _CLI_MUSIC_BGM_TYPES), app/models/schema.py enums, app/services/video.py render branches, webui/Main.py dropdowns, and docs/skill/mpt_agent.py SUPPORTED_SOURCES — inline comments explicitly cite past bugs caused by drift.
- Any new file-serving or file-accepting endpoint must go through file_security.resolve_path_within_directory and _sanitize_upload_filename-style normalization — never join user paths directly; the /tasks StaticFiles mount is guarded by middleware, not router dependencies.
- Task state writes must use sm.state.update_task (create/overwrite) vs patch_task (merge-only, safe against deleted tasks) correctly — redis_manager and cross-post code rely on patch_task's Lua atomicity to avoid resurrecting deleted tasks.
- WebUI config writes must use update_config_nonblocking/delete_config_nonblocking/try_save_config — direct config.toml or config.<section> writes bypass the pending-update queue and can deadlock a page against runtime_config_lock held by a running task.
- Never log credentials: material.py's _redact_secret/_redact_request_error, llm.py's _sanitize_error_message, and the asgi lifespan's 'log only that auth is enabled' pattern are deliberate conventions to extend, not bypass.
- Custom audio file paths are intentionally asymmetric: HTTP/WebUI callers are confined to the task directory; only the CLI passes allow_server_file_input=True — do not widen this for API callers.
- VideoParams defaults that read config.ui are frozen at import time; changing [ui] handling must account for the fact that cli.py deliberately duplicates these defaults to keep CLI behavior stable under invalid saved values.
- Edge subtitle failures must NOT auto-fallback to Whisper (would trigger a multi-GB model download); Whisper loads only on explicit subtitle_provider='whisper' — preserve this when touching subtitle logic.
- Code comments are predominantly Chinese (with some English); docstrings and comments carry unusually detailed rationale for past fixes — match this style and keep the explanations when editing. (inferred)
- Dependencies: pyproject.toml is the source of truth pinned by uv.lock, but requirements.txt must be manually kept in sync for Dockerfile/legacy pip builds — check both when bumping.
- Edge cases documented in code that are easy to regress: config.toml may be a directory (load_config rmtree's it), TOML may have repeated UTF-8 BOMs, queue limits may arrive as TOML strings/floats (handled by _coerce_task_limit), and dequeue must never throw inside the task_done→check_queue chain.