render-engine / app.py
oviet711's picture
Update app.py
7bd3d0d verified
Raw
History Blame Contribute Delete
5.08 kB
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()