""" MoneyPrinterV2 — Hugging Face Spaces Gradio UI Generates YouTube Shorts (video only, no upload) using: - HF Inference API for LLM text generation - Gemini API (Nano Banana 2) for AI image generation - KittenTTS for text-to-speech - faster-whisper for subtitle generation - MoviePy for video assembly """ import os import sys import json import tempfile import shutil import traceback # Ensure src/ is importable (same trick as src/main.py) _root = os.path.dirname(os.path.abspath(__file__)) _src = os.path.join(_root, "src") if _src not in sys.path: sys.path.insert(0, _src) # Set sys.path[0] so config.ROOT_DIR resolves correctly if sys.path[0] != _src: sys.path.insert(0, _src) import gradio as gr from config import assert_folder_structure, ROOT_DIR from llm_provider import select_model, list_models, generate_text from status import info, success, error, warning # Ensure .mp directory exists assert_folder_structure() # Available TTS voices (KittenTTS) TTS_VOICES = ["Jasper", "Bella", "Luna", "Bruno", "Rosie", "Hugo", "Kiki", "Leo"] LANGUAGES = ["English", "Korean", "Spanish", "French", "German", "Japanese", "Chinese", "Portuguese", "Russian", "Arabic"] # LLM model choices LLM_MODELS = list_models() def generate_short( niche: str, language: str, llm_model: str, tts_voice: str, sentence_length: int, progress=gr.Progress(track_tqdm=True), ): """Main generation pipeline — returns (video_path, metadata_json, log_text).""" log_lines = [] def log(msg): log_lines.append(msg) if not niche.strip(): return None, {}, "Please enter a niche/topic." # Select LLM model select_model(llm_model) log(f"Using LLM model: {llm_model}") # Override TTS voice via env (config reads it) os.environ["TTS_VOICE"] = tts_voice # Override script sentence length os.environ["SCRIPT_SENTENCE_LENGTH"] = str(int(sentence_length)) try: # Import YouTube class (browser-free mode) from classes.YouTube import YouTube from classes.Tts import TTS log("Initializing YouTube pipeline (browser-free)...") yt = YouTube( account_uuid="gradio-session", account_nickname="gradio-user", fp_profile_path="", niche=niche, language=language, use_browser=False, ) # Step 1: Generate topic log("Generating topic...") topic = yt.generate_topic() log(f"Topic: {topic}") # Step 2: Generate script log("Generating script...") script = yt.generate_script() log(f"Script: {script[:200]}...") # Step 3: Generate metadata log("Generating metadata (title, description)...") metadata = yt.generate_metadata() log(f"Title: {metadata['title']}") # Step 4: Generate image prompts log("Generating image prompts...") prompts = yt.generate_prompts() log(f"Generated {len(prompts)} image prompts") # Step 5: Generate images log("Generating images...") generated_count = 0 for i, prompt in enumerate(prompts): log(f" Image {i+1}/{len(prompts)}: {prompt[:80]}...") result = yt.generate_image(prompt) if result: generated_count += 1 log(f"Generated {generated_count}/{len(prompts)} images") if generated_count == 0: return None, metadata, "\n".join(log_lines + ["ERROR: No images were generated. Check your GEMINI_API_KEY."]) # Step 6: TTS log("Generating speech (TTS)...") tts = TTS() yt.generate_script_to_speech(tts) log("TTS complete") # Step 7: Combine into video log("Combining into final video (this may take a few minutes)...") video_path = yt.combine() log(f"Video generated: {video_path}") full_metadata = { "title": metadata["title"], "description": metadata["description"], "topic": topic, "script": script, "image_prompts": prompts, "images_generated": generated_count, } return video_path, full_metadata, "\n".join(log_lines) except Exception as e: log_lines.append(f"ERROR: {e}") log_lines.append(traceback.format_exc()) return None, {}, "\n".join(log_lines) # --------------------------------------------------------------------------- # Gradio UI # --------------------------------------------------------------------------- with gr.Blocks(title="MoneyPrinterV2 — YouTube Shorts Generator", theme=gr.themes.Soft()) as demo: gr.Markdown("# MoneyPrinterV2 — YouTube Shorts Generator") gr.Markdown( "Generate YouTube Shorts videos automatically using AI. " "The pipeline generates a topic, script, images, speech, subtitles, and assembles them into a video." ) with gr.Row(): with gr.Column(scale=1): niche_input = gr.Textbox( label="Niche / Topic", placeholder="e.g. 'artificial intelligence', 'cooking tips', 'space exploration'", lines=2, ) language_input = gr.Dropdown( choices=LANGUAGES, value="English", label="Language", ) llm_model_input = gr.Dropdown( choices=LLM_MODELS, value=LLM_MODELS[0] if LLM_MODELS else "", label="LLM Model", ) tts_voice_input = gr.Dropdown( choices=TTS_VOICES, value="Jasper", label="TTS Voice", ) sentence_length_input = gr.Slider( minimum=2, maximum=8, value=4, step=1, label="Script Sentence Count", ) generate_btn = gr.Button("Generate Video", variant="primary", size="lg") with gr.Column(scale=2): video_output = gr.Video(label="Generated Video") metadata_output = gr.JSON(label="Metadata") log_output = gr.Textbox(label="Progress Log", lines=15, interactive=False) generate_btn.click( fn=generate_short, inputs=[niche_input, language_input, llm_model_input, tts_voice_input, sentence_length_input], outputs=[video_output, metadata_output, log_output], ) gr.Markdown( "---\n" "**Required HF Space Secrets:** `HF_TOKEN` (for LLM), `GEMINI_API_KEY` (for image generation)\n\n" "**Note:** This demo generates videos only. YouTube upload requires browser automation and is not available on HF Spaces." ) if __name__ == "__main__": demo.launch()