akhaliq HF Staff commited on
Commit
24c348f
Β·
verified Β·
1 Parent(s): 0ac76ac

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +67 -209
app.py CHANGED
@@ -23,7 +23,7 @@ def verify_pro_status(token: Optional[Union[gr.OAuthToken, str]]) -> bool:
23
  elif isinstance(token, str):
24
  token_str = token
25
  else:
26
- return False # Should not happen with correct type hints, but for safety
27
 
28
  try:
29
  user_info = whoami(token=token_str)
@@ -52,12 +52,12 @@ def cleanup_temp_files():
52
 
53
  def generate_video(
54
  prompt: str,
55
- duration: int = 8, # These are not used by the fal.ai sora-2 model directly, but kept for interface consistency
56
- size: str = "1280x720", # These are not used by the fal.ai sora-2 model directly, but kept for interface consistency
57
  api_key: Optional[str] = None
58
  ) -> Tuple[Optional[str], str]:
59
  """
60
- Generate video using Sora-2 Text-to-Video through Hugging Face Inference API with fal-ai provider.
61
  Returns tuple of (video_path, status_message).
62
  """
63
  # Clean up old files before generating new ones
@@ -74,15 +74,16 @@ def generate_video(
74
  else:
75
  temp_client = client
76
  if not os.environ.get("HF_TOKEN") and not api_key:
77
- return None, "❌ Please set HF_TOKEN environment variable or provide an API key."
78
 
79
  # Call Sora-2 through Hugging Face Inference API
80
  video_bytes = temp_client.text_to_video(
81
  prompt,
82
- model="akhaliq/sora-2", # Specific model for text-to-video
83
  )
84
 
85
  # Save to temporary file with proper cleanup
 
86
  temp_file = tempfile.NamedTemporaryFile(suffix=".mp4", delete=False)
87
  try:
88
  temp_file.write(video_bytes)
@@ -98,65 +99,12 @@ def generate_video(
98
  error_msg = f"❌ Error generating video: {str(e)}"
99
  return None, error_msg
100
 
101
- def generate_image_to_video(
102
- image_path: str,
103
- prompt: str,
104
- api_key: Optional[str] = None
105
- ) -> Tuple[Optional[str], str]:
106
- """
107
- Generate video using Sora-2 Image-to-Video through Hugging Face Inference API with fal-ai provider.
108
- Returns tuple of (video_path, status_message).
109
- """
110
- cleanup_temp_files() # Clean up old files
111
-
112
- if not image_path:
113
- return None, "❌ Please upload an image."
114
- if not prompt or prompt.strip() == "":
115
- return None, "❌ Please enter a prompt for the video generation."
116
-
117
- try:
118
- if api_key:
119
- temp_client = InferenceClient(
120
- provider="fal-ai",
121
- api_key=api_key,
122
- bill_to="huggingface",
123
- )
124
- else:
125
- temp_client = client
126
- if not os.environ.get("HF_TOKEN") and not api_key:
127
- return None, "❌ Please set HF_TOKEN environment variable or provide an API key."
128
-
129
- with open(image_path, "rb") as image_file:
130
- input_image_bytes = image_file.read()
131
-
132
- video_bytes = temp_client.image_to_video(
133
- input_image_bytes,
134
- prompt=prompt,
135
- model="akhaliq/sora-2-image-to-video", # Specific model for image-to-video
136
- )
137
-
138
- temp_file = tempfile.NamedTemporaryFile(suffix=".mp4", delete=False)
139
- try:
140
- temp_file.write(video_bytes)
141
- temp_file.flush()
142
- video_path = temp_file.name
143
- finally:
144
- temp_file.close()
145
-
146
- status_message = f"βœ… Video generated successfully from image and prompt!"
147
- return video_path, status_message
148
-
149
- except Exception as e:
150
- error_msg = f"❌ Error generating video from image: {str(e)}"
151
- return None, error_msg
152
-
153
-
154
  def generate_with_pro_auth(
155
  prompt: str,
156
- oauth_token: Optional[gr.OAuthToken] = None # Gradio will auto-inject this based on type hint
157
  ) -> Tuple[Optional[str], str]:
158
  """
159
- Wrapper function that checks if user is PRO before generating text-to-video.
160
  """
161
  # Check if user is PRO
162
  if not verify_pro_status(oauth_token):
@@ -176,47 +124,14 @@ def generate_with_pro_auth(
176
 
177
  return video_path, status
178
 
179
- def generate_image_to_video_with_pro_auth(
180
- image_path: str,
181
- prompt: str,
182
- oauth_token: Optional[gr.OAuthToken] = None # Gradio will auto-inject this based on type hint
183
- ) -> Tuple[Optional[str], str]:
184
- """
185
- Wrapper function that checks if user is PRO before generating image-to-video.
186
- """
187
- if not verify_pro_status(oauth_token):
188
- raise gr.Error("Access Denied. This app is exclusively for Hugging Face PRO users. Please subscribe to PRO to use this app.")
189
-
190
- if not image_path:
191
- return None, "❌ Please upload an image."
192
- if not prompt or prompt.strip() == "":
193
- return None, "❌ Please enter a prompt"
194
-
195
- video_path, status = generate_image_to_video(
196
- image_path,
197
- prompt,
198
- api_key=None # This will use the environment HF_TOKEN
199
- )
200
-
201
- return video_path, status
202
-
203
-
204
  def simple_generate(prompt: str) -> Optional[str]:
205
- """Simplified wrapper for text-to-video examples that only returns video."""
206
  if not prompt or prompt.strip() == "":
207
  return None
208
 
209
  video_path, _ = generate_video(prompt, duration=8, size="1280x720", api_key=None)
210
  return video_path
211
 
212
- def simple_generate_image_to_video(image_path: str, prompt: str) -> Optional[str]:
213
- """Simplified wrapper for image-to-video examples that only returns video."""
214
- if not image_path or not prompt or prompt.strip() == "":
215
- return None
216
-
217
- video_path, _ = generate_image_to_video(image_path, prompt, api_key=None)
218
- return video_path
219
-
220
  def create_ui():
221
  """Create the Gradio interface with PRO verification."""
222
 
@@ -237,11 +152,11 @@ def create_ui():
237
  }
238
  '''
239
 
240
- with gr.Blocks(title="Sora-2 Text & Image-to-Video Generator", theme=gr.themes.Soft(), css=css) as demo:
241
  gr.HTML("""
242
  <div style="text-align: center; max-width: 800px; margin: 0 auto;">
243
  <h1 style="font-size: 2.5em; margin-bottom: 0.5em;">
244
- 🎬 Sora-2 Text & Image-to-Video Generator
245
  <span class="pro-badge">PRO</span>
246
  </h1>
247
  <p style="font-size: 1.1em; color: #666; margin-bottom: 20px;">Generate stunning videos using OpenAI's Sora-2 model</p>
@@ -271,115 +186,62 @@ def create_ui():
271
  </div>
272
  """)
273
 
274
- with gr.Tabs() as tab_selector:
275
- with gr.TabItem("Text-to-Video", id=0):
276
- with gr.Row():
277
- with gr.Column(scale=1):
278
- prompt_input_text = gr.Textbox(
279
- label="Enter your text prompt",
280
- placeholder="Describe the video you want to create...",
281
- lines=4
282
- )
283
-
284
- with gr.Accordion("Advanced Settings", open=False):
285
- gr.Markdown("*Coming soon: Duration and resolution controls*")
286
-
287
- generate_btn_text = gr.Button("πŸŽ₯ Generate Video from Text", variant="primary", size="lg")
288
-
289
- with gr.Column(scale=1):
290
- video_output_text = gr.Video(
291
- label="Generated Video",
292
- height=400,
293
- interactive=False,
294
- show_download_button=True
295
- )
296
- status_output_text = gr.Textbox(
297
- label="Status",
298
- interactive=False,
299
- visible=True
300
- )
301
-
302
- # Examples section with queue disabled
303
- gr.Examples(
304
- examples=[
305
- "A serene beach at sunset with waves gently rolling onto the shore",
306
- "A butterfly emerging from its chrysalis in slow motion",
307
- "Northern lights dancing across a starry night sky",
308
- "A bustling city street transitioning from day to night in timelapse",
309
- "A close-up of coffee being poured into a cup with steam rising",
310
- "Cherry blossoms falling in slow motion in a Japanese garden"
311
- ],
312
- inputs=prompt_input_text,
313
- outputs=video_output_text,
314
- fn=simple_generate, # Examples use simplified function
315
- cache_examples=False,
316
- api_name=False,
317
- show_api=False,
318
  )
319
 
320
- # Event handler for generation with queue disabled
321
- generate_btn_text.click(
322
- fn=generate_with_pro_auth,
323
- inputs=[prompt_input_text], # OAuth token is auto-injected by type hint
324
- outputs=[video_output_text, status_output_text],
325
- queue=False,
326
- api_name=False,
327
- show_api=False,
328
- )
329
-
330
- with gr.TabItem("Image-to-Video", id=1):
331
- with gr.Row():
332
- with gr.Column(scale=1):
333
- image_input = gr.Image(
334
- label="Upload your input image",
335
- type="filepath",
336
- height=300,
337
- value="https://huggingface.co/spaces/akhaliq/sora-2/raw/main/cat.png" # Example image
338
- )
339
- prompt_input_image = gr.Textbox(
340
- label="Enter your text prompt for the video",
341
- placeholder="Describe the action or style you want for the video (e.g., 'The cat starts to dance')",
342
- lines=3
343
- )
344
-
345
- generate_btn_image = gr.Button("πŸ–ΌοΈ Generate Video from Image", variant="primary", size="lg")
346
-
347
- with gr.Column(scale=1):
348
- video_output_image = gr.Video(
349
- label="Generated Video",
350
- height=400,
351
- interactive=False,
352
- show_download_button=True
353
- )
354
- status_output_image = gr.Textbox(
355
- label="Status",
356
- interactive=False,
357
- visible=True
358
- )
359
-
360
- gr.Examples(
361
- examples=[
362
- ["https://huggingface.co/spaces/akhaliq/sora-2/raw/main/cat.png", "The cat starts to dance"],
363
- ["https://huggingface.co/spaces/akhaliq/sora-2/raw/main/forest.png", "A magical forest where trees shimmer with light"],
364
- ["https://huggingface.co/spaces/akhaliq/sora-2/raw/main/car.png", "A classic car driving through a futuristic city"]
365
- ],
366
- inputs=[image_input, prompt_input_image],
367
- outputs=video_output_image,
368
- fn=simple_generate_image_to_video,
369
- cache_examples=False,
370
- api_name=False,
371
- show_api=False,
372
  )
373
-
374
- generate_btn_image.click(
375
- fn=generate_image_to_video_with_pro_auth,
376
- inputs=[image_input, prompt_input_image], # OAuth token is auto-injected by type hint
377
- outputs=[video_output_image, status_output_image],
378
- queue=False,
379
- api_name=False,
380
- show_api=False,
381
  )
382
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
383
  # Footer
384
  gr.HTML("""
385
  <div style="text-align: center; margin-top: 40px; padding: 20px; border-top: 1px solid #e0e0e0;">
@@ -388,9 +250,7 @@ def create_ui():
388
  """)
389
 
390
  def control_access(profile: Optional[gr.OAuthProfile] = None, oauth_token: Optional[gr.OAuthToken] = None):
391
- """Control interface visibility based on PRO status.
392
- Gradio automatically injects gr.OAuthProfile and gr.OAuthToken based on type hints
393
- when OAuth is enabled for the Space."""
394
  if not profile:
395
  # User not logged in
396
  return gr.update(visible=False), gr.update(visible=False)
@@ -403,12 +263,12 @@ def create_ui():
403
  message = """
404
  ## ✨ Exclusive Access for PRO Users
405
 
406
- Thank you for your interest in the Sora-2 Text & Image-to-Video Generator!
407
 
408
  This advanced AI video generation tool is available exclusively for Hugging Face **PRO** members.
409
 
410
  ### What you get with PRO:
411
- - βœ… Unlimited access to Sora-2 video generation (Text-to-Video & Image-to-Video)
412
  - βœ… High-quality video outputs up to 1280x720
413
  - βœ… Fast generation times with priority queue
414
  - βœ… Access to other exclusive PRO Spaces
@@ -440,11 +300,9 @@ def create_ui():
440
  return gr.update(visible=False), gr.update(visible=True, value=message)
441
 
442
  # Check access on load
443
- # No explicit inputs are needed here as gr.OAuthProfile and gr.OAuthToken are
444
- # provided automatically by Gradio to the function based on type hints.
445
  demo.load(
446
  control_access,
447
- inputs=None, # Removed explicit instantiation of OAuthProfile and OAuthToken
448
  outputs=[main_interface, pro_message]
449
  )
450
 
 
23
  elif isinstance(token, str):
24
  token_str = token
25
  else:
26
+ return False
27
 
28
  try:
29
  user_info = whoami(token=token_str)
 
52
 
53
  def generate_video(
54
  prompt: str,
55
+ duration: int = 8,
56
+ size: str = "1280x720",
57
  api_key: Optional[str] = None
58
  ) -> Tuple[Optional[str], str]:
59
  """
60
+ Generate video using Sora-2 through Hugging Face Inference API with fal-ai provider.
61
  Returns tuple of (video_path, status_message).
62
  """
63
  # Clean up old files before generating new ones
 
74
  else:
75
  temp_client = client
76
  if not os.environ.get("HF_TOKEN") and not api_key:
77
+ return None, "❌ Please set HF_TOKEN environment variable."
78
 
79
  # Call Sora-2 through Hugging Face Inference API
80
  video_bytes = temp_client.text_to_video(
81
  prompt,
82
+ model="akhaliq/sora-2",
83
  )
84
 
85
  # Save to temporary file with proper cleanup
86
+ # Use NamedTemporaryFile with delete=True but keep reference
87
  temp_file = tempfile.NamedTemporaryFile(suffix=".mp4", delete=False)
88
  try:
89
  temp_file.write(video_bytes)
 
99
  error_msg = f"❌ Error generating video: {str(e)}"
100
  return None, error_msg
101
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
  def generate_with_pro_auth(
103
  prompt: str,
104
+ oauth_token: Optional[gr.OAuthToken] = None
105
  ) -> Tuple[Optional[str], str]:
106
  """
107
+ Wrapper function that checks if user is PRO before generating video.
108
  """
109
  # Check if user is PRO
110
  if not verify_pro_status(oauth_token):
 
124
 
125
  return video_path, status
126
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
127
  def simple_generate(prompt: str) -> Optional[str]:
128
+ """Simplified wrapper for examples that only returns video."""
129
  if not prompt or prompt.strip() == "":
130
  return None
131
 
132
  video_path, _ = generate_video(prompt, duration=8, size="1280x720", api_key=None)
133
  return video_path
134
 
 
 
 
 
 
 
 
 
135
  def create_ui():
136
  """Create the Gradio interface with PRO verification."""
137
 
 
152
  }
153
  '''
154
 
155
+ with gr.Blocks(title="Sora-2 Text-to-Video Generator", theme=gr.themes.Soft(), css=css) as demo:
156
  gr.HTML("""
157
  <div style="text-align: center; max-width: 800px; margin: 0 auto;">
158
  <h1 style="font-size: 2.5em; margin-bottom: 0.5em;">
159
+ 🎬 Sora-2 Text-to-Video Generator
160
  <span class="pro-badge">PRO</span>
161
  </h1>
162
  <p style="font-size: 1.1em; color: #666; margin-bottom: 20px;">Generate stunning videos using OpenAI's Sora-2 model</p>
 
186
  </div>
187
  """)
188
 
189
+ with gr.Row():
190
+ with gr.Column(scale=1):
191
+ prompt_input = gr.Textbox(
192
+ label="Enter your prompt",
193
+ placeholder="Describe the video you want to create...",
194
+ lines=4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
195
  )
196
 
197
+ with gr.Accordion("Advanced Settings", open=False):
198
+ gr.Markdown("*Coming soon: Duration and resolution controls*")
199
+
200
+ generate_btn = gr.Button("πŸŽ₯ Generate Video", variant="primary", size="lg")
201
+
202
+ with gr.Column(scale=1):
203
+ video_output = gr.Video(
204
+ label="Generated Video",
205
+ height=400,
206
+ interactive=False,
207
+ show_download_button=True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
208
  )
209
+ status_output = gr.Textbox(
210
+ label="Status",
211
+ interactive=False,
212
+ visible=True
 
 
 
 
213
  )
214
 
215
+ # Hidden manual token input removed - not needed anymore
216
+
217
+ # Examples section with queue disabled
218
+ gr.Examples(
219
+ examples=[
220
+ "A serene beach at sunset with waves gently rolling onto the shore",
221
+ "A butterfly emerging from its chrysalis in slow motion",
222
+ "Northern lights dancing across a starry night sky",
223
+ "A bustling city street transitioning from day to night in timelapse",
224
+ "A close-up of coffee being poured into a cup with steam rising",
225
+ "Cherry blossoms falling in slow motion in a Japanese garden"
226
+ ],
227
+ inputs=prompt_input,
228
+ outputs=video_output,
229
+ fn=simple_generate, # Examples use simplified function
230
+ cache_examples=False,
231
+ api_name=False,
232
+ show_api=False,
233
+ )
234
+
235
+ # Event handler for generation with queue disabled
236
+ generate_btn.click(
237
+ fn=generate_with_pro_auth,
238
+ inputs=[prompt_input],
239
+ outputs=[video_output, status_output],
240
+ queue=False,
241
+ api_name=False,
242
+ show_api=False,
243
+ )
244
+
245
  # Footer
246
  gr.HTML("""
247
  <div style="text-align: center; margin-top: 40px; padding: 20px; border-top: 1px solid #e0e0e0;">
 
250
  """)
251
 
252
  def control_access(profile: Optional[gr.OAuthProfile] = None, oauth_token: Optional[gr.OAuthToken] = None):
253
+ """Control interface visibility based on PRO status."""
 
 
254
  if not profile:
255
  # User not logged in
256
  return gr.update(visible=False), gr.update(visible=False)
 
263
  message = """
264
  ## ✨ Exclusive Access for PRO Users
265
 
266
+ Thank you for your interest in the Sora-2 Text-to-Video Generator!
267
 
268
  This advanced AI video generation tool is available exclusively for Hugging Face **PRO** members.
269
 
270
  ### What you get with PRO:
271
+ - βœ… Unlimited access to Sora-2 video generation
272
  - βœ… High-quality video outputs up to 1280x720
273
  - βœ… Fast generation times with priority queue
274
  - βœ… Access to other exclusive PRO Spaces
 
300
  return gr.update(visible=False), gr.update(visible=True, value=message)
301
 
302
  # Check access on load
 
 
303
  demo.load(
304
  control_access,
305
+ inputs=None,
306
  outputs=[main_interface, pro_message]
307
  )
308