sharul20001 commited on
Commit
6417316
·
verified ·
1 Parent(s): 9db7407

Update downloader.py

Browse files
Files changed (1) hide show
  1. downloader.py +119 -57
downloader.py CHANGED
@@ -1,26 +1,11 @@
1
- import os, sys, time, pathlib
 
 
 
 
2
  from openai import OpenAI, BadRequestError
3
 
4
- API_KEY = os.getenv("OPENAI_API_KEY")
5
- if not API_KEY:
6
- print("ERROR: set OPENAI_API_KEY dulu.")
7
- sys.exit(1)
8
-
9
- client = OpenAI(api_key=API_KEY)
10
-
11
- MODEL = "sora-2"
12
- PROMPT = (
13
- "YouTuber, black t-shirt, in a professional studio setup, sitting at his desk "
14
- "talking directly into a Shure SM7B microphone on a boom arm. He says with a smirk: "
15
- "'Wait... did an AI just generate me to teach you about AI? That's pretty meta...' "
16
- "Dark background with a soft blue LED light glow. Professional lighting setup with key light and rim light. "
17
- "Shot on Sony FX6, cinematic depth of field, crisp focus, vertical format 9:16."
18
- )
19
- SIZE = "720x1280"
20
- SECONDS = "12" # wajib string dan hanya boleh "4" | "8" | "12"
21
-
22
- outdir = pathlib.Path("output")
23
- outdir.mkdir(parents=True, exist_ok=True)
24
 
25
  def as_percent(v):
26
  try:
@@ -29,39 +14,116 @@ def as_percent(v):
29
  except Exception:
30
  return 0.0
31
 
32
- try:
33
- job = client.videos.create(model=MODEL, prompt=PROMPT, size=SIZE, seconds=SECONDS)
34
- print(f"Video job created: {job.id} status={job.status}")
35
- except BadRequestError as e:
36
- s = str(e)
37
- if "billing_hard_limit_reached" in s or "insufficient_quota" in s:
38
- print("Kredit/limit akun habis. Tambah limit di Billing atau pakai API key lain.")
39
- else:
40
- print("Gagal membuat video:", s)
41
- sys.exit(1)
42
-
43
- # polling
44
- bar_len = 30
45
- while True:
46
- job = client.videos.retrieve(job.id)
47
- status = getattr(job, "status", "unknown")
48
- progress = as_percent(getattr(job, "progress", 0))
49
- filled = int((progress / 100) * bar_len)
50
- bar = "=" * filled + "-" * (bar_len - filled)
51
- sys.stdout.write(f"\r{status:<11} [{bar}] {progress:5.1f}%")
52
- sys.stdout.flush()
53
- if status in {"succeeded", "failed", "canceled"}:
54
- break
55
- time.sleep(2)
56
- print()
57
-
58
- if job.status != "succeeded":
59
- msg = getattr(getattr(job, "error", None), "message", job.status)
60
- print("Video generation failed:", msg)
61
- sys.exit(1)
62
-
63
- print("Downloading video...")
64
- blob = client.videos.download_content(job.id, variant="video")
65
- outfile = outdir / f"{job.id}.mp4"
66
- blob.write_to_file(str(outfile))
67
- print("Saved:", outfile)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import time
3
+ import pathlib
4
+ import shutil
5
+ import gradio as gr
6
  from openai import OpenAI, BadRequestError
7
 
8
+ ALLOWED_SECONDS = {"4", "8", "12"}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
 
10
  def as_percent(v):
11
  try:
 
14
  except Exception:
15
  return 0.0
16
 
17
+ def generate_video(prompt, seconds, size, model, user_api_key, trim_to_10s):
18
+ # Ambil API key: dari input user atau dari env
19
+ api_key = (user_api_key or os.getenv("OPENAI_API_KEY") or "").strip()
20
+ if not api_key:
21
+ raise gr.Error("Masukkan OpenAI API key atau set OPENAI_API_KEY di Secrets.")
22
+
23
+ # seconds wajib STRING dan salah satu dari 4/8/12
24
+ seconds = str(seconds).strip()
25
+ if seconds not in ALLOWED_SECONDS:
26
+ raise gr.Error("Durasi 'seconds' harus salah satu dari: 4, 8, atau 12 detik.")
27
+
28
+ client = OpenAI(api_key=api_key)
29
+
30
+ # 1) Create job
31
+ try:
32
+ job = client.videos.create(
33
+ model=model,
34
+ prompt=prompt,
35
+ size=size,
36
+ seconds=seconds, # penting: string "4" | "8" | "12"
37
+ )
38
+ except BadRequestError as e:
39
+ s = str(e)
40
+ if "billing_hard_limit_reached" in s or "insufficient_quota" in s:
41
+ raise gr.Error("Gagal: limit/kredit akun OpenAI habis. Tambah limit di Billing atau gunakan API key lain.")
42
+ if "invalid_type" in s and "seconds" in s:
43
+ raise gr.Error("Gagal: parameter 'seconds' harus string ('4','8','12'), bukan angka.")
44
+ raise gr.Error(f"Gagal membuat video: {s}")
45
+
46
+ yield f"Job dibuat: {job.id}. Status: {job.status}", None
47
+
48
+ # 2) Polling progress
49
+ last_progress = -1
50
+ while True:
51
+ time.sleep(2)
52
+ job = client.videos.retrieve(job.id)
53
+ status = getattr(job, "status", "unknown")
54
+ progress = as_percent(getattr(job, "progress", 0))
55
+ if int(progress) != int(last_progress):
56
+ yield f"{status} - {progress:.1f}%", None
57
+ last_progress = progress
58
+
59
+ if status == "succeeded":
60
+ break
61
+ if status in {"failed", "canceled"}:
62
+ msg = getattr(getattr(job, "error", None), "message", status)
63
+ raise gr.Error(f"Gagal: {msg}")
64
+
65
+ # 3) Download hasil
66
+ tmp_dir = pathlib.Path("/tmp")
67
+ tmp_dir.mkdir(exist_ok=True, parents=True)
68
+ raw_path = tmp_dir / f"{job.id}.mp4"
69
+
70
+ blob = client.videos.download_content(job.id, variant="video")
71
+ blob.write_to_file(str(raw_path))
72
+
73
+ out_path = raw_path
74
+
75
+ # 4) Opsi: trim ke 10 detik (pakai ffmpeg jika tersedia)
76
+ if trim_to_10s:
77
+ ffmpeg = shutil.which("ffmpeg")
78
+ if ffmpeg:
79
+ trimmed_path = tmp_dir / f"{job.id}_10s.mp4"
80
+ # -t 10: batasi durasi 10 detik; -c copy: tanpa re-encode (cepat)
81
+ # jika format tidak mengizinkan -c copy, fallback re-encode
82
+ code = os.system(f'{ffmpeg} -y -i "{raw_path}" -t 10 -c copy "{trimmed_path}"')
83
+ if code != 0:
84
+ # fallback re-encode
85
+ os.system(f'{ffmpeg} -y -i "{raw_path}" -t 10 -vf "scale=iw:ih" -c:v libx264 -preset veryfast -crf 20 -c:a aac -b:a 128k "{trimmed_path}"')
86
+ if trimmed_path.exists():
87
+ out_path = trimmed_path
88
+ else:
89
+ yield "ffmpeg tidak tersedia, melewati proses trim. Hasil tetap durasi asli.", None
90
+
91
+ yield "Selesai ✅", str(out_path)
92
+
93
+
94
+ with gr.Blocks(title="Sora-2 Video Generator") as demo:
95
+ gr.Markdown("## Sora-2 Video Generator (HF Space) — BYOK ready")
96
+ gr.Markdown("- Durasi wajib: 4, 8, atau 12 detik (aturan API). Jika butuh 10 detik, centang Trim to 10s setelah render.")
97
+ with gr.Row():
98
+ api = gr.Textbox(label="OpenAI API Key (opsional)", type="password", placeholder="sk-...")
99
+ model = gr.Textbox(value="sora-2", label="Model")
100
+ prompt = gr.Textbox(
101
+ label="Prompt",
102
+ lines=8,
103
+ value=(
104
+ "YouTuber, black t-shirt, in a professional studio setup, sitting at his desk "
105
+ "talking directly into a Shure SM7B microphone on a boom arm. He says with a smirk: "
106
+ "'Wait... did an AI just generate me to teach you about AI? That's pretty meta...' "
107
+ "Dark background with a soft blue LED light glow. Professional lighting setup with key light and rim light. "
108
+ "Shot on Sony FX6, cinematic depth of field, crisp focus, vertical format 9:16."
109
+ ),
110
+ )
111
+ with gr.Row():
112
+ size = gr.Dropdown(
113
+ ["720x1280", "1080x1920", "1280x720", "1920x1080"],
114
+ value="720x1280",
115
+ label="Resolution",
116
+ )
117
+ seconds = gr.Dropdown(["4", "8", "12"], value="12", label="Duration (sec)")
118
+ trim10 = gr.Checkbox(value=False, label="Trim to 10s after render")
119
+ run = gr.Button("Generate")
120
+ status = gr.Textbox(label="Status", lines=2)
121
+ video = gr.Video(label="Hasil (unduh dari tombol di player)")
122
+
123
+ run.click(
124
+ fn=generate_video,
125
+ inputs=[prompt, seconds, size, model, api, trim10],
126
+ outputs=[status, video],
127
+ )
128
+
129
+ demo.launch()