Spaces:
Running
Running
File size: 6,810 Bytes
a8fdab7 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 | """
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()
|