Spaces:
Runtime error
Runtime error
| import gradio as gr | |
| import os | |
| import re | |
| import glob | |
| import requests | |
| from moviepy.editor import ImageClip, AudioFileClip, CompositeAudioClip | |
| # ================================ | |
| # Config | |
| # ================================ | |
| HF_TOKEN = os.getenv("HF_API_TOKEN") # Hugging Face API token | |
| OWNER_TOKEN = os.getenv("OWNER_TOKEN") # Your private owner token | |
| SD_MODEL = "runwayml/stable-diffusion-v1-5" | |
| # ================================ | |
| # Utility Functions | |
| # ================================ | |
| def slugify(text): | |
| """Convert text into safe lowercase filename parts.""" | |
| text = text.lower() | |
| text = re.sub(r'[^a-z0-9]+', '_', text) | |
| return text.strip("_") | |
| def get_next_file(base_name): | |
| """Increment file names: journal_001.mp4, 002, etc.""" | |
| existing = glob.glob(f"{base_name}_*.mp4") | |
| if not existing: | |
| return f"{base_name}_001.mp4" | |
| nums = [int(re.search(r'(\d+).mp4', f).group(1)) for f in existing if re.search(r'(\d+).mp4', f)] | |
| next_num = max(nums) + 1 if nums else 1 | |
| return f"{base_name}_{next_num:03d}.mp4" | |
| def generate_image(prompt): | |
| """Call Hugging Face API to generate an image.""" | |
| headers = {"Authorization": f"Bearer {HF_TOKEN}"} | |
| payload = {"inputs": prompt} | |
| response = requests.post( | |
| f"https://api-inference.huggingface.co/models/{SD_MODEL}", | |
| headers=headers, | |
| json=payload | |
| ) | |
| if response.status_code == 200: | |
| out_path = "generated.png" | |
| with open(out_path, "wb") as f: | |
| f.write(response.content) | |
| return out_path | |
| else: | |
| raise RuntimeError(f"Image generation failed: {response.text}") | |
| # ================================ | |
| # Main Video Creation | |
| # ================================ | |
| def create_video(prompt, journal_text, duration, bg_music, user_music, add_voice, owner_token): | |
| status = "π Starting video generation..." | |
| # -------- Access Rules -------- | |
| if owner_token != OWNER_TOKEN: | |
| journal_text = "" # hide journal for public | |
| if duration > 10: | |
| return None, None, "β οΈ Free users can only make videos up to 10s." | |
| else: | |
| if duration > 600: | |
| return None, None, "β οΈ Owner limit is 600s (10 minutes). Reduce duration." | |
| try: | |
| # -------- Generate image -------- | |
| status = "πΌοΈ Generating image..." | |
| image_path = generate_image(prompt) | |
| # -------- Create video clip -------- | |
| status = "π¬ Creating video clip..." | |
| clip = ImageClip(image_path, duration=duration) | |
| # Force 1080p, 30fps | |
| clip = clip.resize(height=1080).set_fps(30) | |
| # -------- Background music -------- | |
| audio_clips = [] | |
| if user_music is not None: | |
| audio_clips.append(AudioFileClip(user_music)) | |
| if bg_music != "none": | |
| try: | |
| audio_clips.append(AudioFileClip(f"music/{bg_music}.mp3")) | |
| except: | |
| pass | |
| if audio_clips: | |
| final_audio = CompositeAudioClip(audio_clips).set_duration(clip.duration) | |
| clip = clip.set_audio(final_audio) | |
| # -------- Save video -------- | |
| out_file = get_next_file("journal") | |
| clip.write_videofile(out_file, codec="libx264", audio_codec="aac") | |
| return out_file, image_path, "β Video generation complete!" | |
| except Exception as e: | |
| return None, None, f"β Error: {str(e)}" | |
| # ================================ | |
| # Gradio Interface | |
| # ================================ | |
| def gradio_interface(prompt, journal_text, duration, bg_music, user_music, add_voice, owner_token): | |
| return create_video(prompt, journal_text, duration, bg_music, user_music, add_voice, owner_token) | |
| with gr.Blocks() as demo: | |
| gr.Markdown("# π₯ EchoHeart Video Generator") | |
| gr.Markdown("Generate AI-powered videos with EchoHeart. β¨") | |
| with gr.Row(): | |
| prompt = gr.Textbox(label="Prompt", placeholder="Describe your scene...") | |
| journal_text = gr.Textbox(label="Journal (owner only)", type="text") | |
| duration = gr.Slider(1, 600, value=5, step=1, label="Duration (seconds)") | |
| bg_music = gr.Dropdown(choices=["none", "lofi", "cinematic", "happy"], value="none", label="Background Music") | |
| user_music = gr.File(label="Upload Your Own Music (optional)", type="file") | |
| add_voice = gr.Checkbox(label="Add Voice (owner only)") | |
| owner_token = gr.Textbox(label="Owner Token (if you have one)", type="password") | |
| generate_btn = gr.Button("π Generate Video") | |
| video_out = gr.Video(label="Generated Video") | |
| image_out = gr.Image(label="Generated Image") | |
| status_out = gr.Textbox(label="Status") | |
| generate_btn.click( | |
| fn=gradio_interface, | |
| inputs=[prompt, journal_text, duration, bg_music, user_music, add_voice, owner_token], | |
| outputs=[video_out, image_out, status_out] | |
| ) | |
| demo.launch() |