File size: 5,081 Bytes
a312ba3
 
d444fec
a312ba3
a3186c3
 
5740d5f
85ade34
a312ba3
d444fec
 
 
80e5d31
69cd013
85ade34
 
5740d5f
 
85ade34
 
5740d5f
85ade34
5740d5f
 
 
 
 
 
 
85ade34
 
5740d5f
85ade34
 
 
 
 
 
 
 
 
 
 
 
 
a3186c3
69cd013
7bd3d0d
 
 
 
d444fec
80e5d31
d444fec
 
 
ebe0347
80e5d31
7bd3d0d
 
85ade34
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ebe0347
 
5740d5f
85ade34
 
5740d5f
d444fec
85ade34
69cd013
d444fec
69cd013
 
 
 
5740d5f
69cd013
ebe0347
85ade34
 
69cd013
ebe0347
69cd013
5740d5f
 
 
 
 
85ade34
5740d5f
 
 
 
 
d444fec
 
85ade34
 
d444fec
85ade34
80e5d31
d444fec
a312ba3
d444fec
98dbe18
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
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()