CodeVetter · Repo Unpack · Behavior traces
harry0703/MoneyPrinterTurbo
Three flows were traced end-to-end. API startup goes main.py → asgi app construction → lifespan recovery. The primary action (video generation) is identical across frontends: a state record is created before scheduling, _run_pipeline walks preflight→script→terms→audio→subtitle→materials→video, and cross-posting is scheduled only after the task is COMPLETE. Persistence flows through two channels — task state (memory dict or Redis hashes) and task artifacts (atomic JSON writes into storage/tasks/<id>) — plus config.toml for settings.
- API startup trace: `python main.py` → uvicorn.run('app.asgi:app') → module-level get_application() builds routes/middleware/static mounts → parse_cors_allowed_origins(CORS_ALLOWED_ORIGINS) → lifespan logs startup, warns when app.api_key is empty, and calls task_service.recover_interrupted_cross_posts() to fail orphaned pending/processing cross-posts whose owner process no longer exists.
- First-run config trace: importing app.config.config → load_config() → if config.toml is a directory it is deleted, if missing it is copied from config.example.toml → _load_toml_config parses with a utf-8-sig BOM fallback → section dicts are wrapped in _SynchronizedConfig → ffmpeg_path (if set) is exported as IMAGEIO_FFMPEG_EXE for imageio/moviepy.
- API video-generation trace: POST /api/v1/videos → verify_token dependency → create_task → utils.get_uuid() → sm.state.update_task(task_id) → task_manager.add_task(tm.start, ...) → TaskManager.execute_task spawns a thread running run_task → tm.start → _run_pipeline → on completion update_task(COMPLETE, progress=100, videos=...) → optional _schedule_cross_post.
- Pipeline order inside _run_pipeline: (0) preflight — provider is_enabled() checks for seedance/ofox/metaso_minimax/muapi/openai_image, music-provider key+prompt-length+access validation, then utils.check_ffmpeg_ready() unless stop_at is script/terms; (1) generate_script → (2) generate_terms (skipped for local source) → save_script_data → (3) generate_audio → (4) generate_subtitle → (5) get_video_materials → (6) generate_final_videos → (7) mark COMPLETE + schedule cross-post.
- stop-at truncation trace: each stage boundary checks stop_at and returns early with COMPLETE state — 'script' returns {script}, 'terms' returns {script,terms}, 'audio' returns {audio_file,audio_duration}, 'subtitle' returns {subtitle_path}, 'materials' returns {materials}; the API exposes this as the /subtitle and /audio endpoints and the CLI as --stop-at.
- WebUI generation trace: Main.py button handler → _prepare_generation_task() → webui_task.submit_generation() writes state record before scheduling → InMemoryTaskManager(max_concurrent_tasks=1) → _run_generation attaches a thread-filtered loguru sink, acquires config.runtime_config_lock(), calls tm.start → a @st.fragment(run_every=0.5s) polls webui_task.get_task_logs() to stream logs to the page.
- CLI trace: `python cli.py ...` → _force_utf8_console() → run_cli() → parse_args() (custom argparse types validate colors, speeds, UUID task ids, subtitle enums) → params built by merging CLI flags over [ui] config defaults → tm.start(allow_server_file_input=True) → prints one JSON result object to stdout; batch mode merges each manifest object over CLI defaults and runs tasks sequentially.
- Failure trace: any unhandled exception inside _run_pipeline is caught in start(), logged, and converted by _mark_task_failed into state=-1 with failed_stage and error — visible via GET /tasks/{id}; the CLI maps a failed result to exit code 1, argument/manifest errors to exit code 2.
- Task-deletion trace: DELETE /api/v1/tasks/{id} → sm.state.get_task → tm.is_task_busy refuses deletion while state==PROCESSING or cross_post_state in {pending,processing} (409) → shutil.rmtree(storage/tasks/<id>) → sm.state.delete_task.
- Cross-post recovery trace: each cross-post future is registered in _cross_post_futures keyed by task_id with owner 'hostname:pid:uuid' persisted in task state; on startup or deletion checks, _is_cross_post_owner_alive inspects the owner PID (Win32 OpenProcess on Windows, os.kill(pid,0) elsewhere, conservative-alive for foreign hosts) and stranded states are marked failed.