akhaliq HF Staff commited on
Commit
10616f8
·
verified ·
1 Parent(s): 8d392f5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +79 -125
app.py CHANGED
@@ -3,7 +3,7 @@ import os
3
  import tempfile
4
  import shutil
5
  from typing import Optional, Tuple, Union
6
- from huggingface_hub import InferenceClient, whoami
7
  from pathlib import Path
8
 
9
  # Initialize Hugging Face Inference Client with fal-ai provider
@@ -13,28 +13,6 @@ client = InferenceClient(
13
  bill_to="huggingface",
14
  )
15
 
16
- def verify_pro_status(token: Optional[Union[gr.OAuthToken, str]]) -> bool:
17
- """Verifies if the user is a Hugging Face PRO user or part of an enterprise org."""
18
- if not token:
19
- return False
20
-
21
- if isinstance(token, gr.OAuthToken):
22
- token_str = token.token
23
- elif isinstance(token, str):
24
- token_str = token
25
- else:
26
- return False
27
-
28
- try:
29
- user_info = whoami(token=token_str)
30
- return (
31
- user_info.get("isPro", False) or
32
- any(org.get("isEnterprise", False) for org in user_info.get("orgs", []))
33
- )
34
- except Exception as e:
35
- print(f"Could not verify user's PRO/Enterprise status: {e}")
36
- return False
37
-
38
  def cleanup_temp_files():
39
  """Clean up old temporary video files to prevent storage overflow."""
40
  try:
@@ -88,7 +66,6 @@ def generate_video(
88
  except Exception as e:
89
  return None, f"❌ Error generating video: {str(e)}"
90
 
91
- # --- NEW: image -> video support ---
92
  def generate_video_from_image(
93
  image: Union[str, bytes],
94
  prompt: str,
@@ -136,13 +113,13 @@ def generate_video_from_image(
136
  except Exception as e:
137
  return None, f"❌ Error generating video from image: {str(e)}"
138
 
139
- def generate_with_pro_auth(
140
  prompt: str,
141
- oauth_token: Optional[gr.OAuthToken] = None
142
  ) -> Tuple[Optional[str], str]:
143
- """Wrapper function that checks if user is PRO before generating video."""
144
- if not verify_pro_status(oauth_token):
145
- raise gr.Error("Access Denied. This app is exclusively for Hugging Face PRO users.")
146
 
147
  if not prompt or prompt.strip() == "":
148
  return None, "❌ Please enter a prompt"
@@ -154,42 +131,24 @@ def generate_with_pro_auth(
154
  api_key=None
155
  )
156
 
157
- # --- NEW: PRO-gated wrapper for image -> video ---
158
- def generate_with_pro_auth_image(
159
  prompt: str,
160
- image_path: Optional[str] = None,
161
- oauth_token: Optional[gr.OAuthToken] = None
162
  ) -> Tuple[Optional[str], str]:
163
- """Checks PRO status then calls image->video generator."""
164
- if not verify_pro_status(oauth_token):
165
- raise gr.Error("Access Denied. This app is exclusively for Hugging Face PRO users.")
166
  if not image_path:
167
  return None, "❌ Please upload an image"
168
  return generate_video_from_image(image=image_path, prompt=prompt, api_key=None)
169
 
170
- def simple_generate(prompt: str) -> Optional[str]:
171
- """Simplified wrapper for examples that only returns video."""
172
- if not prompt or prompt.strip() == "":
173
- return None
174
- video_path, _ = generate_video(prompt, duration=8, size="1280x720", api_key=None)
175
- return video_path
176
-
177
  def create_ui():
178
  css = '''
179
  .logo-dark{display: none}
180
  .dark .logo-dark{display: block !important}
181
  .dark .logo-light{display: none}
182
  #sub_title{margin-top: -20px !important}
183
- .pro-badge{
184
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
185
- color: white;
186
- padding: 4px 12px;
187
- border-radius: 20px;
188
- font-size: 0.9em;
189
- font-weight: bold;
190
- display: inline-block;
191
- margin-left: 8px;
192
- }
193
  '''
194
 
195
  with gr.Blocks(title="Sora-2 Text-to-Video Generator", theme=gr.themes.Soft(), css=css) as demo:
@@ -197,89 +156,77 @@ def create_ui():
197
  <div style="text-align: center; max-width: 800px; margin: 0 auto;">
198
  <h1 style="font-size: 2.5em; margin-bottom: 0.5em;">
199
  🎬 Sora-2 Text-to-Video Generator
200
- <span class="pro-badge">PRO</span>
201
  </h1>
202
  <p style="font-size: 1.1em; color: #666; margin-bottom: 20px;">Generate stunning videos using OpenAI's Sora-2 model</p>
203
- <p id="sub_title" style="font-size: 1em; margin-top: 20px; margin-bottom: 15px;">
204
- <strong>Exclusive access for Hugging Face PRO users.</strong>
205
- <a href="http://huggingface.co/subscribe/pro?source=sora2_video" target="_blank" style="color: #667eea;">Subscribe to PRO →</a>
206
- </p>
207
  <p style="font-size: 0.9em; color: #999; margin-top: 15px;">
208
  Built with <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" style="color: #667eea;">anycoder</a>
209
  </p>
210
  </div>
211
  """)
212
 
 
213
  gr.LoginButton()
214
- pro_message = gr.Markdown(visible=False)
215
- main_interface = gr.Column(visible=False)
216
 
217
- with main_interface:
218
- gr.HTML("""<div style="text-align: center; margin: 20px 0;">
219
- <p style="color: #28a745; font-weight: bold;">✨ Welcome PRO User! You have full access to Sora-2.</p>
220
- </div>""")
221
-
222
- # Text -> Video
223
- with gr.Row():
224
- with gr.Column(scale=1):
225
- prompt_input = gr.Textbox(
226
- label="Enter your prompt",
227
- placeholder="Describe the video you want to create...",
228
- lines=4
229
- )
230
- generate_btn = gr.Button("🎥 Generate Video", variant="primary", size="lg")
231
- with gr.Column(scale=1):
232
- video_output = gr.Video(label="Generated Video", height=400, interactive=False, show_download_button=True)
233
- status_output = gr.Textbox(label="Status", interactive=False, visible=True)
234
-
235
- generate_btn.click(
236
- fn=generate_with_pro_auth,
237
- inputs=[prompt_input],
238
- outputs=[video_output, status_output],
239
- queue=False
240
- )
241
-
242
- # --- NEW: Image -> Video UI ---
243
- gr.HTML("""
244
- <div style="text-align: center; margin: 40px 0 10px;">
245
- <h3 style="margin-bottom: 8px;">🖼️ ➜ 🎬 Image → Video (beta)</h3>
246
- <p style="color:#666; margin:0;">Turn a single image into a short video with a guiding prompt.</p>
247
- </div>
248
- """)
249
- with gr.Row():
250
- with gr.Column(scale=1):
251
- img_prompt_input = gr.Textbox(
252
- label="Describe how the scene should evolve",
253
- placeholder="e.g., The cat starts to dance and spins playfully",
254
- lines=3,
255
- )
256
- image_input = gr.Image(label="Upload an image", type="filepath")
257
- generate_img_btn = gr.Button("🎥 Generate from Image", variant="primary")
258
- with gr.Column(scale=1):
259
- video_output_img = gr.Video(label="Generated Video (from Image)", height=400, interactive=False, show_download_button=True)
260
- status_output_img = gr.Textbox(label="Status", interactive=False, visible=True)
261
-
262
- generate_img_btn.click(
263
- fn=generate_with_pro_auth_image,
264
- inputs=[img_prompt_input, image_input],
265
- outputs=[video_output_img, status_output_img],
266
- queue=False
267
- )
268
-
269
- gr.HTML("""<div style="text-align: center; margin-top: 40px; padding: 20px; border-top: 1px solid #e0e0e0;">
270
- <h3 style="color: #667eea;">Thank you for being a PRO user! 🤗</h3>
271
- </div>""")
272
 
273
- def control_access(profile: Optional[gr.OAuthProfile] = None, oauth_token: Optional[gr.OAuthToken] = None):
274
- if not profile:
275
- return gr.update(visible=False), gr.update(visible=False)
276
- if verify_pro_status(oauth_token):
277
- return gr.update(visible=True), gr.update(visible=False)
278
- else:
279
- message = "## ✨ Exclusive Access for PRO Users\n\nThis tool is available exclusively for Hugging Face **PRO** members."
280
- return gr.update(visible=False), gr.update(visible=True, value=message)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
281
 
282
- demo.load(control_access, inputs=None, outputs=[main_interface, pro_message])
 
 
 
 
 
 
 
 
 
 
 
283
 
284
  return demo
285
 
@@ -292,4 +239,11 @@ if __name__ == "__main__":
292
  print(f"Initial cleanup error: {e}")
293
 
294
  app = create_ui()
295
- app.launch(show_api=False, enable_monitoring=False, quiet=True, max_threads=10)
 
 
 
 
 
 
 
 
3
  import tempfile
4
  import shutil
5
  from typing import Optional, Tuple, Union
6
+ from huggingface_hub import InferenceClient
7
  from pathlib import Path
8
 
9
  # Initialize Hugging Face Inference Client with fal-ai provider
 
13
  bill_to="huggingface",
14
  )
15
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  def cleanup_temp_files():
17
  """Clean up old temporary video files to prevent storage overflow."""
18
  try:
 
66
  except Exception as e:
67
  return None, f"❌ Error generating video: {str(e)}"
68
 
 
69
  def generate_video_from_image(
70
  image: Union[str, bytes],
71
  prompt: str,
 
113
  except Exception as e:
114
  return None, f"❌ Error generating video from image: {str(e)}"
115
 
116
+ def generate_with_auth(
117
  prompt: str,
118
+ profile: gr.OAuthProfile | None
119
  ) -> Tuple[Optional[str], str]:
120
+ """Wrapper function that checks if user is logged in before generating video."""
121
+ if profile is None:
122
+ raise gr.Error("Click Sign in with Hugging Face button to use this app for free")
123
 
124
  if not prompt or prompt.strip() == "":
125
  return None, "❌ Please enter a prompt"
 
131
  api_key=None
132
  )
133
 
134
+ def generate_with_auth_image(
 
135
  prompt: str,
136
+ image_path: Optional[str],
137
+ profile: gr.OAuthProfile | None
138
  ) -> Tuple[Optional[str], str]:
139
+ """Checks login status then calls image->video generator."""
140
+ if profile is None:
141
+ raise gr.Error("Click Sign in with Hugging Face button to use this app for free")
142
  if not image_path:
143
  return None, "❌ Please upload an image"
144
  return generate_video_from_image(image=image_path, prompt=prompt, api_key=None)
145
 
 
 
 
 
 
 
 
146
  def create_ui():
147
  css = '''
148
  .logo-dark{display: none}
149
  .dark .logo-dark{display: block !important}
150
  .dark .logo-light{display: none}
151
  #sub_title{margin-top: -20px !important}
 
 
 
 
 
 
 
 
 
 
152
  '''
153
 
154
  with gr.Blocks(title="Sora-2 Text-to-Video Generator", theme=gr.themes.Soft(), css=css) as demo:
 
156
  <div style="text-align: center; max-width: 800px; margin: 0 auto;">
157
  <h1 style="font-size: 2.5em; margin-bottom: 0.5em;">
158
  🎬 Sora-2 Text-to-Video Generator
 
159
  </h1>
160
  <p style="font-size: 1.1em; color: #666; margin-bottom: 20px;">Generate stunning videos using OpenAI's Sora-2 model</p>
161
+ <p style='color: orange;'>⚠️ You must Sign in with Hugging Face using the button to use this app.</p>
 
 
 
162
  <p style="font-size: 0.9em; color: #999; margin-top: 15px;">
163
  Built with <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" style="color: #667eea;">anycoder</a>
164
  </p>
165
  </div>
166
  """)
167
 
168
+ # Add login button - required for OAuth
169
  gr.LoginButton()
 
 
170
 
171
+ # Text -> Video
172
+ with gr.Row():
173
+ with gr.Column(scale=1):
174
+ prompt_input = gr.Textbox(
175
+ label="Enter your prompt",
176
+ placeholder="Describe the video you want to create...",
177
+ lines=4
178
+ )
179
+ generate_btn = gr.Button("🎥 Generate Video", variant="primary", size="lg")
180
+ with gr.Column(scale=1):
181
+ video_output = gr.Video(label="Generated Video", height=400, interactive=False, show_download_button=True)
182
+ status_output = gr.Textbox(label="Status", interactive=False, visible=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
183
 
184
+ generate_btn.click(
185
+ fn=generate_with_auth,
186
+ inputs=[prompt_input],
187
+ outputs=[video_output, status_output],
188
+ queue=False
189
+ )
190
+
191
+ # Image -> Video UI
192
+ gr.HTML("""
193
+ <div style="text-align: center; margin: 40px 0 10px;">
194
+ <h3 style="margin-bottom: 8px;">🖼️ ➜ 🎬 Image → Video (beta)</h3>
195
+ <p style="color:#666; margin:0;">Turn a single image into a short video with a guiding prompt.</p>
196
+ </div>
197
+ """)
198
+ with gr.Row():
199
+ with gr.Column(scale=1):
200
+ img_prompt_input = gr.Textbox(
201
+ label="Describe how the scene should evolve",
202
+ placeholder="e.g., The cat starts to dance and spins playfully",
203
+ lines=3,
204
+ )
205
+ image_input = gr.Image(label="Upload an image", type="filepath")
206
+ generate_img_btn = gr.Button("🎥 Generate from Image", variant="primary")
207
+ with gr.Column(scale=1):
208
+ video_output_img = gr.Video(label="Generated Video (from Image)", height=400, interactive=False, show_download_button=True)
209
+ status_output_img = gr.Textbox(label="Status", interactive=False, visible=True)
210
+
211
+ generate_img_btn.click(
212
+ fn=generate_with_auth_image,
213
+ inputs=[img_prompt_input, image_input],
214
+ outputs=[video_output_img, status_output_img],
215
+ queue=False
216
+ )
217
 
218
+ # Example usage guidance
219
+ gr.Examples(
220
+ examples=[
221
+ "A majestic golden eagle soaring through a vibrant sunset sky"
222
+ ],
223
+ inputs=prompt_input,
224
+ outputs=video_output,
225
+ fn=generate_video, # Examples use the original function
226
+ cache_examples=False,
227
+ api_name=False,
228
+ show_api=False,
229
+ )
230
 
231
  return demo
232
 
 
239
  print(f"Initial cleanup error: {e}")
240
 
241
  app = create_ui()
242
+ # Launch without special auth parameters
243
+ # OAuth is enabled via Space metadata (hf_oauth: true in README.md)
244
+ app.launch(
245
+ show_api=False,
246
+ enable_monitoring=False,
247
+ quiet=True,
248
+ max_threads=10
249
+ )