Spaces:
Sleeping
Sleeping
| import os | |
| import gradio as gr | |
| import subprocess | |
| import cv2 | |
| from PIL import Image | |
| import google.generativeai as genai | |
| import base64 | |
| # ========== SETUP ========== | |
| def set_api_key(api_key): | |
| os.environ["GOOGLE_API_KEY"] = api_key | |
| genai.configure(api_key=api_key) | |
| # ========== TEXT TO IMAGE ========== | |
| def text_to_image(api_key, prompt, image_file, resolution): | |
| try: | |
| set_api_key(api_key) | |
| model = genai.GenerativeModel("imagen-3.0") | |
| if image_file: | |
| final_prompt = {"text": prompt, "image": Image.open(image_file)} | |
| else: | |
| final_prompt = prompt | |
| result = model.generate_content(final_prompt) | |
| if not result.candidates: | |
| return None, "β Gagal: tiada kandidat dihasilkan" | |
| image_bytes = result.candidates[0].content.parts[0].inline_data.data | |
| fname = "output_img.png" | |
| with open(fname, "wb") as f: | |
| f.write(base64.b64decode(image_bytes)) | |
| return fname, "β Gambar berjaya dihasilkan!" | |
| except Exception as e: | |
| return None, f"β Error: {str(e)}" | |
| # ========== SHORT VIDEO ========== | |
| def text_to_video(api_key, prompt, image_file, duration, aspect_ratio, resolution): | |
| try: | |
| set_api_key(api_key) | |
| model = genai.GenerativeModel("veo-3") | |
| # aspect ratio & res | |
| if aspect_ratio == "9:16": | |
| res = {"width": 720 if resolution == "720p" else 1080, | |
| "height": 1280 if resolution == "720p" else 1920} | |
| else: | |
| res = {"width": 1280 if resolution == "720p" else 1920, | |
| "height": 720 if resolution == "720p" else 1080} | |
| if image_file: | |
| final_prompt = {"text": prompt, "image": Image.open(image_file)} | |
| else: | |
| final_prompt = prompt | |
| result = model.generate_content({ | |
| "text": prompt, | |
| "config": { | |
| "duration_seconds": int(duration), | |
| "fps": 12, | |
| "video": {"width": res["width"], "height": res["height"]} | |
| } | |
| }) | |
| if not result.candidates: | |
| return None, "β Gagal: tiada kandidat video dihasilkan" | |
| video_bytes = result.candidates[0].content.parts[0].inline_data.data | |
| fname = "output_vid.mp4" | |
| with open(fname, "wb") as f: | |
| f.write(base64.b64decode(video_bytes)) | |
| return fname, "β Video berhasil dibuat!" | |
| except Exception as e: | |
| return None, f"β Error: {str(e)}" | |
| # ========== LONG VIDEO ========== | |
| def auto_segment_long_video(api_key, prompt, start_img, num_segments, duration_each, aspect_ratio, resolution): | |
| try: | |
| set_api_key(api_key) | |
| model = genai.GenerativeModel("veo-3") | |
| if aspect_ratio == "9:16": | |
| res = {"width": 720 if resolution == "720p" else 1080, | |
| "height": 1280 if resolution == "720p" else 1920} | |
| else: | |
| res = {"width": 1280 if resolution == "720p" else 1920, | |
| "height": 720 if resolution == "720p" else 1080} | |
| words = prompt.split() | |
| chunk_size = max(1, len(words) // num_segments) | |
| seg_prompts = [" ".join(words[i:i+chunk_size]) for i in range(0, len(words), chunk_size)][:num_segments] | |
| carry_image = Image.open(start_img) if start_img else None | |
| segment_files = [] | |
| for idx, text in enumerate(seg_prompts): | |
| if carry_image: | |
| final_prompt = {"text": text, "image": carry_image} | |
| else: | |
| final_prompt = text | |
| result = model.generate_content({ | |
| "text": text, | |
| "config": { | |
| "duration_seconds": int(duration_each), | |
| "fps": 12, | |
| "video": {"width": res["width"], "height": res["height"]} | |
| } | |
| }) | |
| if not result.candidates: | |
| return None, f"β Gagal buat segmen {idx}" | |
| video_bytes = result.candidates[0].content.parts[0].inline_data.data | |
| fname = f"seg_{idx}.mp4" | |
| with open(fname, "wb") as f: | |
| f.write(base64.b64decode(video_bytes)) | |
| segment_files.append(fname) | |
| cap = cv2.VideoCapture(fname) | |
| cap.set(cv2.CAP_PROP_POS_FRAMES, cap.get(cv2.CAP_PROP_FRAME_COUNT) - 1) | |
| ret, frame = cap.read() | |
| cap.release() | |
| if ret: | |
| framefile = f"carry_{idx}.jpg" | |
| cv2.imwrite(framefile, frame) | |
| carry_image = Image.open(framefile) | |
| # gabung semua segmen | |
| list_file = "segments.txt" | |
| with open(list_file, "w") as f: | |
| for seg in segment_files: | |
| f.write(f"file '{seg}'\n") | |
| final_out = "final_long.mp4" | |
| subprocess.run( | |
| ["ffmpeg", "-y", "-f", "concat", "-safe", "0", "-i", list_file, "-c", "copy", final_out], | |
| check=True | |
| ) | |
| return final_out, "β Video panjang berhasil dibuat!" | |
| except Exception as e: | |
| return None, f"β Error: {str(e)}" | |
| # ========== UI ========== | |
| with gr.Blocks(css=""" | |
| body {background: linear-gradient(160deg,#0f0c29,#302b63,#24243e);} | |
| .gradio-container {color:white;} | |
| h1,h2,h3,h4,label {color:white !important;} | |
| button {background: linear-gradient(90deg,#6a11cb,#2575fc) !important; color:white !important;} | |
| """) as demo: | |
| gr.HTML("<h1 style='text-align:center'>π₯ Penjana Video VEO</h1>") | |
| api_key = gr.Textbox(label="π Masukkan Kunci API Gemini anda", type="password") | |
| with gr.Tabs(): | |
| with gr.Tab("π¬ Video Pendek"): | |
| prompt_vid = gr.Textbox(label="Prompt", placeholder="Tulis ide video...", lines=4) | |
| vid_file = gr.File(label="Imej Rujukan (Opsional)", file_types=[".jpg",".png"], type="filepath") | |
| duration = gr.Slider(3, 8, value=5, label="Tempoh (detik)") | |
| aspect_ratio = gr.Dropdown(choices=["16:9", "9:16"], value="16:9", label="Nisbah Aspek") | |
| resolution = gr.Dropdown(choices=["720p","1080p"], value="720p", label="Resolusi") | |
| btn_vid = gr.Button("Hasilkan Video Pendek") | |
| out_vid = gr.Video() | |
| msg_vid = gr.Label() | |
| btn_vid.click(fn=text_to_video, | |
| inputs=[api_key, prompt_vid, vid_file, duration, aspect_ratio, resolution], | |
| outputs=[out_vid, msg_vid]) | |
| with gr.Tab("ποΈ Video Panjang"): | |
| full_prompt = gr.Textbox(label="Prompt Narasi Panjang", placeholder="Tulis cerita panjang...", lines=6) | |
| start_img = gr.File(label="Imej Awal (Opsional)", file_types=[".jpg",".png"], type="filepath") | |
| num_segments = gr.Slider(1, 10, value=3, step=1, label="Jumlah Segmen") | |
| dur_each = gr.Slider(3, 8, value=5, step=1, label="Durasi/Segmen (detik)") | |
| aspect_ratio_long = gr.Dropdown(choices=["16:9", "9:16"], value="16:9", label="Nisbah Aspek") | |
| resolution_long = gr.Dropdown(choices=["720p","1080p"], value="720p", label="Resolusi") | |
| btn_long = gr.Button("Hasilkan Video Panjang") | |
| out_long = gr.Video() | |
| msg_long = gr.Label() | |
| btn_long.click(fn=auto_segment_long_video, | |
| inputs=[api_key, full_prompt, start_img, num_segments, dur_each, | |
| aspect_ratio_long, resolution_long], | |
| outputs=[out_long, msg_long]) | |
| with gr.Tab("πΌοΈ Imej"): | |
| prompt_img = gr.Textbox(label="Prompt", placeholder="Tulis ide gambar...", lines=4) | |
| img_file = gr.File(label="Imej Rujukan (Opsional)", file_types=[".jpg",".png"], type="filepath") | |
| resolution_img = gr.Dropdown(choices=["720p","1080p"], value="720p", label="Resolusi (dummy)") | |
| btn_img = gr.Button("Hasilkan Imej") | |
| out_img = gr.Image() | |
| msg_img = gr.Label() | |
| btn_img.click(fn=text_to_image, | |
| inputs=[api_key, prompt_img, img_file, resolution_img], | |
| outputs=[out_img, msg_img]) | |
| if __name__ == "__main__": | |
| demo.launch() |