Spaces:
Runtime error
Runtime error
| import gradio as gr | |
| import subprocess | |
| import json | |
| import os | |
| import requests | |
| import urllib.parse | |
| import time | |
| from huggingface_hub import HfApi, InferenceClient | |
| HF_TOKEN = os.getenv("HF_WRITE_TOKEN") | |
| api = HfApi(token=HF_TOKEN) | |
| DATASET_ID = "oviet711/render-factory" | |
| def generate_ai_image(keyword, chunk_id): | |
| print(f"🎨 [TIER 1] Meminta Visual dari Pollinations: {keyword}") | |
| encoded_prompt = urllib.parse.quote(f"cinematic photography, ultra realistic, highly detailed, {keyword}, dark moody lighting, vertical") | |
| url = f"https://image.pollinations.ai/prompt/{encoded_prompt}?width=1080&height=1920&nologo=true" | |
| # TIER 1: Coba Pollinations AI | |
| for attempt in range(2): | |
| try: | |
| response = requests.get(url, stream=True, timeout=10) | |
| if response.status_code == 200: | |
| bg_filename = f"bg_{chunk_id}.png" | |
| with open(bg_filename, 'wb') as f: | |
| for chunk in response.iter_content(1024): | |
| f.write(chunk) | |
| return bg_filename | |
| except Exception as e: | |
| print(f"⚠️ Pollinations batuk (Percobaan {attempt+1}): {e}") | |
| time.sleep(1) | |
| # TIER 2: Jika Pollinations Mati, otomatis pakai Hugging Face FLUX | |
| print("🔄 [TIER 2] Beralih ke Hugging Face FLUX API...") | |
| try: | |
| client = InferenceClient(model="black-forest-labs/FLUX.1-schnell", token=HF_TOKEN) | |
| prompt = f"cinematic shot, photorealistic, vertical 9:16, {keyword}, dark and moody lighting, masterpiece" | |
| image = client.text_to_image(prompt) | |
| bg_filename = f"bg_{chunk_id}.png" | |
| image.save(bg_filename) | |
| return bg_filename | |
| except Exception as e: | |
| print(f"⚠️ TIER 2 Gagal (Server HF Penuh): {e}") | |
| # TIER 3: Jika semua gagal, return None (FFmpeg akan buat layar hitam) | |
| return None | |
| # HAPUS BARIS INI: import spaces (di bagian atas file) | |
| # HAPUS BARIS INI: @spaces.GPU | |
| def render_worker(json_data): | |
| try: | |
| data = json.loads(json_data) | |
| chunk_id = data.get('chunk_id', '000') | |
| text = data.get('text', 'Teks kosong') | |
| keyword = data.get('keyword', 'fresh juice') | |
| # ... (Sisa kode di bawahnya tetap sama persis, jangan diubah) ... | |
| # LOGIKA DINAMIS: Kamera & Warna berganti berdasarkan ID potongan | |
| cid = int(chunk_id) | |
| # 1. Rotasi Efek Kamera (Zoompan) | |
| camera_effects = [ | |
| "zoompan=z='min(zoom+0.0015,1.15)':d=500:s=1080x1920", # Maju perlahan ke tengah | |
| "zoompan=z='min(zoom+0.0015,1.15)':y='0':d=500:s=1080x1920", # Maju perlahan ke atas | |
| "zoompan=z='min(zoom+0.0015,1.15)':y='ih':d=500:s=1080x1920" # Maju perlahan ke bawah | |
| ] | |
| selected_camera = camera_effects[cid % len(camera_effects)] | |
| # 2. Rotasi Warna Subtitle (Format BGR: Kuning, Cyan, Putih) | |
| text_colors = ["&H00FFFF", "&HFFFF00", "&HFFFFFF"] | |
| selected_color = text_colors[cid % len(text_colors)] | |
| # Audio | |
| audio_file = f"audio_{chunk_id}.mp3" | |
| vtt_file = f"sub_{chunk_id}.vtt" | |
| subprocess.run([ | |
| "edge-tts", "--voice", "id-ID-GadisNeural", "--rate", "+15%", | |
| "--text", text, "--write-media", audio_file, "--write-subtitles", vtt_file | |
| ], check=True) | |
| # Visual | |
| bg_image = generate_ai_image(keyword, chunk_id) | |
| output_filename = f"chunk_{chunk_id}.mp4" | |
| if bg_image: | |
| cmd = [ | |
| "ffmpeg", "-y", | |
| "-loop", "1", "-framerate", "30", | |
| "-i", bg_image, | |
| "-i", audio_file, | |
| # Memasukkan Kamera Dinamis dan Warna Dinamis! | |
| "-vf", f"scale=1080:1920:force_original_aspect_ratio=increase,crop=1080:1920,setsar=1:1,{selected_camera},subtitles={vtt_file}:force_style='FontSize=20,PrimaryColour={selected_color},OutlineColour=&H000000,BorderStyle=1,Outline=2,Shadow=1,Alignment=2,MarginV=450'", | |
| "-c:v", "libx264", "-c:a", "aac", | |
| "-shortest", "-pix_fmt", "yuv420p", output_filename | |
| ] | |
| else: | |
| cmd = [ | |
| "ffmpeg", "-y", | |
| "-f", "lavfi", "-i", "color=c=black:s=1080x1920:d=30", | |
| "-i", audio_file, | |
| "-vf", f"subtitles={vtt_file}:force_style='FontSize=20,PrimaryColour={selected_color},OutlineColour=&H000000,BorderStyle=1,Outline=2,Shadow=1,Alignment=2,MarginV=450'", | |
| "-c:v", "libx264", "-c:a", "aac", | |
| "-shortest", output_filename | |
| ] | |
| subprocess.run(cmd, check=True) | |
| api.upload_file( | |
| path_or_fileobj=output_filename, path_in_repo=f"chunks/{output_filename}", | |
| repo_id=DATASET_ID, repo_type="dataset" | |
| ) | |
| return f"✅ SUKSES Chunk {chunk_id} (Efek Kamera: {cid%3})" | |
| except Exception as e: | |
| return f"❌ ERROR: {str(e)}" | |
| demo = gr.Interface(fn=render_worker, inputs="text", outputs="text") | |
| demo.launch() |