kavehtaheri commited on
Commit
2523ada
·
verified ·
1 Parent(s): f903719

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +127 -178
app.py CHANGED
@@ -4,24 +4,21 @@ from PIL import Image
4
  import numpy as np
5
  import google.generativeai as genai
6
  import time
7
- from gradio_client import Client, handle_file
8
- import requests # Used for API calls
9
- import io # Used to handle image bytes from download
10
- import re # Used for regex parsing of the URL shortcode
11
 
12
  # --- Configuration ---
13
- # Gemini API key
14
- # IMPORTANT: Replace with your actual Gemini API Key for translation
15
- GEMINI_API_KEY = "AIzaSyAKI92YawOKQ1-HRLmvaryMEWk_y4alJgA"
16
-
17
- # One-API Token for Instagram Downloader
18
- # IMPORTANT: Replace with your token from one-api.ir
19
- ONE_API_TOKEN = "268976:66f4f58a2a905"
20
 
21
  # URL to your background image hosted on your Hugging Face Space.
 
 
22
  BACKGROUND_IMAGE_URL = "1.jpg"
23
 
24
- # Global reader - initialize once to save time
25
  reader = None
26
 
27
  def initialize_reader():
@@ -33,89 +30,6 @@ def initialize_reader():
33
  print("EasyOCR model loaded successfully!")
34
  return reader
35
 
36
- # --- MODIFIED FUNCTION TO DOWNLOAD FROM INSTAGRAM USING SHORTCODE ---
37
- def download_instagram_image(url):
38
- """
39
- Downloads the first image from an Instagram post URL using One-API by extracting the shortcode.
40
- Returns a PIL Image object on success or an error message string on failure.
41
- """
42
- if not url or not url.strip():
43
- return "Please enter an Instagram URL."
44
-
45
- # Regex to extract the shortcode from various Instagram URL formats
46
- # It looks for patterns like /p/..., /reel/..., or /tv/...
47
- # Example: https://www.instagram.com/p/DMaqqN_RuqQ/ -> extracts 'DMaqqN_RuqQ'
48
- match = re.search(r"/(?:p|reel|tv)/([a-zA-Z0-9_-]+)", url.strip())
49
-
50
- if not match:
51
- print(f"ERROR: Could not extract shortcode from URL: {url.strip()}")
52
- return "Invalid Instagram URL format. Please provide a valid post or reel URL."
53
-
54
- shortcode = match.group(1)
55
- print(f"DEBUG: Extracted shortcode: {shortcode}")
56
-
57
- # Use the new API endpoint with the 'shortcode' parameter, as per your curl example
58
- api_endpoint = f"https://api.one-api.ir/instagram/v1/post/?shortcode={shortcode}"
59
-
60
- print(f"DEBUG: Calling One-API with endpoint: {api_endpoint}")
61
-
62
- try:
63
- # Set headers as specified in the curl command, especially the token
64
- headers = {
65
- 'one-api-token': ONE_API_TOKEN,
66
- 'accept': 'application/json'
67
- }
68
- response = requests.get(api_endpoint, headers=headers, timeout=30)
69
- response.raise_for_status() # This will raise an HTTPError for 4xx or 5xx status codes
70
-
71
- data = response.json()
72
-
73
- # Check for success and find the image URL in the response
74
- if data.get("ok") and data.get("result"):
75
- media_list = data["result"].get("media", [])
76
- image_url = None
77
- # Find the first item that is an image
78
- for item in media_list:
79
- if item.get("type") == "image":
80
- image_url = item.get("url")
81
- break
82
-
83
- if not image_url:
84
- return "API Error: No image found in the post media."
85
- else:
86
- error_message = data.get("message", "Unknown API error.")
87
- print(f"ERROR: One-API call failed. Message: {error_message}")
88
- return f"API Error: {error_message}"
89
-
90
- print(f"DEBUG: Found image URL: {image_url[:60]}...")
91
-
92
- # Download the actual image content from the URL found
93
- image_response = requests.get(image_url, timeout=30)
94
- image_response.raise_for_status()
95
-
96
- # Open the image from the downloaded bytes and return it as a PIL Image object
97
- image = Image.open(io.BytesIO(image_response.content))
98
- print("DEBUG: Instagram image downloaded and converted to PIL Image successfully.")
99
- return image
100
-
101
- except requests.exceptions.HTTPError as e:
102
- # This will catch errors like 403 Forbidden, 404 Not Found, etc.
103
- error_details = f"API Error ({e.response.status_code}): {e.response.reason}."
104
- try:
105
- # Try to get more specific error message from the API's JSON response
106
- error_json = e.response.json()
107
- error_details += f" Details: {error_json.get('message', 'No additional details.')}"
108
- except requests.exceptions.JSONDecodeError:
109
- error_details += f" Raw response: {e.response.text}"
110
- print(f"ERROR: HTTP error occurred: {error_details}")
111
- return error_details
112
- except requests.exceptions.RequestException as e:
113
- print(f"ERROR: Network error while contacting API or downloading image: {e}")
114
- return f"Network Error: Could not retrieve data. Please check the connection. Details: {e}"
115
- except Exception as e:
116
- print(f"ERROR: An unexpected error occurred in download_instagram_image: {e}")
117
- return f"An unexpected error occurred: {str(e)}"
118
-
119
  def extract_text_from_quote(image):
120
  """Extract text from quote image using EasyOCR"""
121
  if image is None:
@@ -124,11 +38,9 @@ def extract_text_from_quote(image):
124
  try:
125
  reader = initialize_reader()
126
  img_array = np.array(image)
127
- # Using paragraph=True helps group related lines of text
128
  results = reader.readtext(img_array, paragraph=True)
129
 
130
  if results:
131
- # Join all detected text blocks with a space
132
  text_parts = [result[1].strip() for result in results if len(result) >= 2 and result[1].strip()]
133
  if text_parts:
134
  extracted_text = ' '.join(text_parts)
@@ -146,12 +58,13 @@ def translate_extracted(text, lang):
146
  return "No valid text to translate."
147
 
148
  try:
149
- print(f"DEBUG: API Key loaded. Starting translation to {lang}")
150
- genai.configure(api_key=GEMINI_API_KEY)
151
 
152
- for attempt in range(3): # Simple retry logic
153
  try:
154
  model = genai.GenerativeModel('gemini-1.5-flash')
 
155
  prompt = f"""
156
  You are a cool, chill translator with a fun and warm personality, inspired by Persian Twitter style.
157
  Your translations should be natural, slangy, and relatable. Use colloquial words and contractions.
@@ -179,13 +92,15 @@ def translate_extracted(text, lang):
179
  print(f"DEBUG: Translation failed: {str(e)}")
180
  return error_msg
181
 
 
182
  def overlay_text_on_image(translated_text):
183
  """
184
  Sends translated text to the 'textoverimage1' Space and gets the resulting image.
185
  """
 
186
  if not translated_text or "Error" in translated_text or "No valid text" in translated_text:
187
  print("DEBUG: Skipping image overlay due to invalid translated text.")
188
- return None
189
 
190
  try:
191
  print("DEBUG: Initializing client for 'kavehtaheri/textoverimage1'")
@@ -195,12 +110,13 @@ def overlay_text_on_image(translated_text):
195
  print(f"DEBUG: Persian Text: {translated_text}")
196
  print(f"DEBUG: Image URL: {BACKGROUND_IMAGE_URL}")
197
 
 
198
  result_image_path = client.predict(
199
  persian_text=translated_text,
200
- url="",
201
  upload=handle_file(BACKGROUND_IMAGE_URL),
202
- username="",
203
- text_color="Black",
204
  api_name="/overlay_text_on_image"
205
  )
206
 
@@ -209,123 +125,156 @@ def overlay_text_on_image(translated_text):
209
 
210
  except Exception as e:
211
  print(f"ERROR: Could not get image from 'textoverimage1' space. Error: {e}")
 
 
212
  return None
213
 
214
- def clear_all():
215
- """Clear all inputs and outputs"""
216
- return None, "", "Your extracted quote will appear here...", "Words: 0", "Translation will appear here...", None, True
217
-
218
- # --- Main Processing Functions ---
219
- def process_everything(image, lang):
220
- """The core pipeline: OCR -> Translate -> Overlay. Accepts a PIL image."""
221
- if image is None:
222
- return "Please provide an image first.", "Words: 0", "Translation failed: No image provided.", None
223
-
224
- text, wc = extract_text_from_quote(image)
225
- if "No text" in text or "Error" in text:
226
- return text, wc, "Translation failed: No text extracted.", None
227
 
228
- translated = translate_extracted(text, lang)
229
- final_image = overlay_text_on_image(translated)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
 
231
- return text, wc, translated, final_image
 
 
232
 
233
- def process_from_url(url, lang):
234
- """Handler for the Instagram URL workflow."""
235
- gr.Info("Downloading image from Instagram...")
236
- download_result = download_instagram_image(url)
237
-
238
- # Check if the download function returned an error string
239
- if isinstance(download_result, str):
240
- # The download failed, show an error and stop the process
241
- gr.Error(f"Download Failed: {download_result}")
242
- return download_result, "Words: 0", "Process failed.", None, None
243
-
244
- # If successful, it returned a PIL image. Now process it.
245
- gr.Info("Image downloaded! Starting OCR and Translation...")
246
- text, wc, translated, final_image = process_everything(download_result, lang)
247
-
248
- # Return the downloaded image to populate the input image box for user reference
249
- return text, wc, translated, final_image, download_result
250
 
251
  # --- Gradio Interface ---
252
  with gr.Blocks(title="Quote OCR & Overlay", theme=gr.themes.Soft()) as demo:
253
 
254
  gr.Markdown("# 📝 Quote Text Extractor & Image Generator")
255
- gr.Markdown("Extract text from an image, translate it, and overlay it onto a new background. Choose your input method below.")
256
 
257
  with gr.Row():
258
  # --- INPUT COLUMN ---
259
  with gr.Column(scale=1):
260
- with gr.Tabs():
261
- with gr.Tab("1. Upload Image"):
262
- image_input = gr.Image(label="Upload Quote Image", type="pil", sources=["upload", "clipboard"])
263
- auto_process_cb = gr.Checkbox(label="Auto-Process After Upload", value=True)
264
-
265
- with gr.Tab("1. Download from Instagram URL"):
266
- insta_url_input = gr.Textbox(label="Instagram Post URL", placeholder="Paste a link like https://www.instagram.com/p/C0123ABCD.../")
267
- insta_process_btn = gr.Button("Download & Process", variant="primary")
268
-
269
  target_lang = gr.Dropdown(
270
  label="Target Language",
271
- choices=["persian(farsi)"],
272
  value="persian(farsi)",
273
- interactive=False
274
  )
275
-
276
- with gr.Row():
277
- clear_btn = gr.Button("Clear All", variant="secondary")
278
- extract_btn = gr.Button("Process Uploaded Image", variant="primary")
279
-
280
  # --- OUTPUTS COLUMN ---
281
  with gr.Column(scale=2):
282
  text_output = gr.Textbox(label="2. Extracted English Text", placeholder="Extracted text appears here...", lines=4, show_copy_button=True)
283
  word_count = gr.Textbox(label="Word Count", interactive=False, max_lines=1)
284
  translated_output = gr.Textbox(label="3. Translated Persian Text", placeholder="Persian translation appears here...", lines=4, show_copy_button=True)
285
- gr.Markdown("---")
 
 
286
  final_image_output = gr.Image(label="4. Final Image with Text Overlay", type="filepath")
287
 
288
  # --- Event Handlers ---
289
 
290
- # 1. For the "Process Uploaded Image" button
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
291
  extract_btn.click(
292
  fn=process_everything,
293
- inputs=[image_input, target_lang],
294
  outputs=[text_output, word_count, translated_output, final_image_output]
295
  )
296
 
297
- # 2. For auto-processing when an image is uploaded
298
- def auto_process_wrapper(image, lang, is_enabled):
299
- if is_enabled:
300
- return process_everything(image, lang)
301
- # If auto-process is off, just do OCR and stop
302
- text, wc = extract_text_from_quote(image)
303
- return text, wc, "Translation will appear here...", None
304
-
305
  image_input.change(
306
- fn=auto_process_wrapper,
307
- inputs=[image_input, target_lang, auto_process_cb],
308
  outputs=[text_output, word_count, translated_output, final_image_output]
309
  )
310
 
311
- # 3. For the "Download & Process" button in the Instagram tab
312
- insta_process_btn.click(
313
- fn=process_from_url,
314
- inputs=[insta_url_input, target_lang],
315
- # The outputs now include the image_input to show the downloaded image
316
- outputs=[text_output, word_count, translated_output, final_image_output, image_input]
317
  )
318
 
319
- # 4. For the Clear button
320
  clear_btn.click(
321
  fn=clear_all,
322
- outputs=[image_input, insta_url_input, text_output, word_count, translated_output, final_image_output, auto_process_cb]
323
  )
324
 
325
- gr.Markdown("### 💡 How It Works:\n1. **Upload an image** OR **paste an Instagram URL**.\n2. The app automatically extracts the English text using OCR.\n3. The text is translated to a casual, modern Persian.\n4. The Persian text is sent to a second app which overlays it on a background image.\n5. The final image is displayed.")
326
 
327
  if __name__ == "__main__":
328
- # Ensure these packages are in your requirements.txt:
329
  # gradio
330
  # easyocr
331
  # pillow
@@ -333,4 +282,4 @@ if __name__ == "__main__":
333
  # google-generativeai
334
  # gradio_client
335
  # requests
336
- demo.launch(debug=True)
 
4
  import numpy as np
5
  import google.generativeai as genai
6
  import time
7
+ from gradio_client import Client, handle_file # <-- ADDED IMPORT
8
+ import requests
9
+ from urllib.parse import urlparse
10
+ import io
11
 
12
  # --- Configuration ---
13
+ # Gemini API key - It's better to use environment variables, but this works for now.
14
+ api_key = "AIzaSyAKI92YawOKQ1-HRLmvaryMEWk_y4alJgA"
 
 
 
 
 
15
 
16
  # URL to your background image hosted on your Hugging Face Space.
17
+ # IMPORTANT: Replace 'YOUR-HF-USERNAME/YOUR-SPACE-NAME' with your actual space details.
18
+ # The file '1.jpg' must be in the root of your Space's repository.
19
  BACKGROUND_IMAGE_URL = "1.jpg"
20
 
21
+ # Global reader - initialize once
22
  reader = None
23
 
24
  def initialize_reader():
 
30
  print("EasyOCR model loaded successfully!")
31
  return reader
32
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  def extract_text_from_quote(image):
34
  """Extract text from quote image using EasyOCR"""
35
  if image is None:
 
38
  try:
39
  reader = initialize_reader()
40
  img_array = np.array(image)
 
41
  results = reader.readtext(img_array, paragraph=True)
42
 
43
  if results:
 
44
  text_parts = [result[1].strip() for result in results if len(result) >= 2 and result[1].strip()]
45
  if text_parts:
46
  extracted_text = ' '.join(text_parts)
 
58
  return "No valid text to translate."
59
 
60
  try:
61
+ print(f"DEBUG: API Key loaded (first 5 chars: {api_key[:5]}...). Starting translation to {lang}")
62
+ genai.configure(api_key=api_key)
63
 
64
+ for attempt in range(3):
65
  try:
66
  model = genai.GenerativeModel('gemini-1.5-flash')
67
+ # Updated prompt for clarity and conciseness
68
  prompt = f"""
69
  You are a cool, chill translator with a fun and warm personality, inspired by Persian Twitter style.
70
  Your translations should be natural, slangy, and relatable. Use colloquial words and contractions.
 
92
  print(f"DEBUG: Translation failed: {str(e)}")
93
  return error_msg
94
 
95
+ # NEW FUNCTION TO CALL THE SECOND HF SPACE
96
  def overlay_text_on_image(translated_text):
97
  """
98
  Sends translated text to the 'textoverimage1' Space and gets the resulting image.
99
  """
100
+ # Don't proceed if translation failed or is empty
101
  if not translated_text or "Error" in translated_text or "No valid text" in translated_text:
102
  print("DEBUG: Skipping image overlay due to invalid translated text.")
103
+ return None # Return None to clear the image output
104
 
105
  try:
106
  print("DEBUG: Initializing client for 'kavehtaheri/textoverimage1'")
 
110
  print(f"DEBUG: Persian Text: {translated_text}")
111
  print(f"DEBUG: Image URL: {BACKGROUND_IMAGE_URL}")
112
 
113
+ # The handle_file function downloads the URL to a temporary file for upload
114
  result_image_path = client.predict(
115
  persian_text=translated_text,
116
+ url="", # Pass an empty string for URL if upload is used
117
  upload=handle_file(BACKGROUND_IMAGE_URL),
118
+ username="", # Not needed based on the API, can be empty
119
+ text_color="Black", # As specified in the screenshot
120
  api_name="/overlay_text_on_image"
121
  )
122
 
 
125
 
126
  except Exception as e:
127
  print(f"ERROR: Could not get image from 'textoverimage1' space. Error: {e}")
128
+ # Return a placeholder or raise an error in Gradio UI
129
+ # For now, we return None which will clear the output
130
  return None
131
 
132
+ # NEW FUNCTION TO FETCH IMAGE FROM INSTAGRAM VIA ONE-API
133
+ def get_instagram_image(ig_url):
134
+ """Fetch image from Instagram post using One-API."""
135
+ if not ig_url:
136
+ return None
 
 
 
 
 
 
 
 
137
 
138
+ try:
139
+ # Extract shortcode from URL
140
+ parsed = urlparse(ig_url)
141
+ path = parsed.path.strip('/').split('/')
142
+ if 'p' in path:
143
+ idx = path.index('p')
144
+ if idx + 1 < len(path):
145
+ shortcode = path[idx + 1]
146
+ else:
147
+ raise ValueError("Invalid Instagram URL: No shortcode found.")
148
+ else:
149
+ raise ValueError("Invalid Instagram URL: No 'p' segment found.")
150
+
151
+ # One-API details
152
+ api_token = "268976:66f4f58a2a905"
153
+ api_url = f"https://api.one-api.ir/instagram/v1/post/?shortcode={shortcode}"
154
+ headers = {
155
+ "accept": "application/json",
156
+ "one-api-token": api_token
157
+ }
158
+
159
+ response = requests.get(api_url, headers=headers)
160
+ response.raise_for_status() # Raise if not 200
161
+
162
+ data = response.json()
163
+
164
+ # Adjust this based on actual API response structure (check docs: https://docs.one-api.ir/instagram)
165
+ # Assuming the image URL is in data['url'] or data['thumbnail_src'] (common for IG posts)
166
+ image_url = data.get('url') or data.get('thumbnail_src') or data.get('display_url')
167
+ if not image_url:
168
+ raise ValueError("No image URL found in API response.")
169
+
170
+ # Download the image
171
+ img_response = requests.get(image_url)
172
+ img_response.raise_for_status()
173
+
174
+ img = Image.open(io.BytesIO(img_response.content))
175
+ return img
176
 
177
+ except Exception as e:
178
+ print(f"ERROR fetching Instagram image: {str(e)}")
179
+ return None
180
 
181
+ def clear_all():
182
+ """Clear all inputs and outputs"""
183
+ return None, None, "Your extracted quote will appear here...", "Words: 0", "Translation will appear here...", None, True # Clear image, ig_url, and reset checkbox
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
 
185
  # --- Gradio Interface ---
186
  with gr.Blocks(title="Quote OCR & Overlay", theme=gr.themes.Soft()) as demo:
187
 
188
  gr.Markdown("# 📝 Quote Text Extractor & Image Generator")
189
+ gr.Markdown("Upload an image to extract text, translate it, and overlay it onto a new background.")
190
 
191
  with gr.Row():
192
  # --- INPUT COLUMN ---
193
  with gr.Column(scale=1):
194
+ image_input = gr.Image(label="1. Upload Quote Image", type="pil", sources=["upload", "clipboard"])
195
+ ig_input = gr.Textbox(label="Or Instagram Link", placeholder="e.g., https://www.instagram.com/p/C-ODQjyy4N3/")
196
+ with gr.Row():
197
+ clear_btn = gr.Button("Clear All", variant="secondary")
198
+ extract_btn = gr.Button("Extract & Translate", variant="primary")
199
+
 
 
 
200
  target_lang = gr.Dropdown(
201
  label="Target Language",
202
+ choices=["persian(farsi)"], # Locked to Persian as per the logic
203
  value="persian(farsi)",
204
+ interactive=False # Not changeable since the prompt is hardcoded for Persian
205
  )
206
+ auto_translate = gr.Checkbox(label="Auto-Process After Upload", value=True)
207
+
 
 
 
208
  # --- OUTPUTS COLUMN ---
209
  with gr.Column(scale=2):
210
  text_output = gr.Textbox(label="2. Extracted English Text", placeholder="Extracted text appears here...", lines=4, show_copy_button=True)
211
  word_count = gr.Textbox(label="Word Count", interactive=False, max_lines=1)
212
  translated_output = gr.Textbox(label="3. Translated Persian Text", placeholder="Persian translation appears here...", lines=4, show_copy_button=True)
213
+
214
+ gr.Markdown("---") # Separator
215
+
216
  final_image_output = gr.Image(label="4. Final Image with Text Overlay", type="filepath")
217
 
218
  # --- Event Handlers ---
219
 
220
+ # Combined function for the main button and auto-processing
221
+ def process_everything(image, ig_url, lang, auto_process_enabled):
222
+ if not auto_process_enabled:
223
+ # If auto-process is off, we still run extract but not the rest
224
+ text, wc = extract_text_from_quote(image)
225
+ return text, wc, "Translation will appear here...", None
226
+
227
+ # Determine source image: prioritize uploaded image if provided, else IG link
228
+ if image is not None:
229
+ source_image = image
230
+ elif ig_url:
231
+ source_image = get_instagram_image(ig_url)
232
+ if source_image is None:
233
+ return "Error fetching image from Instagram link.", "Words: 0", "Translation failed: No image fetched.", None
234
+ else:
235
+ return "Please upload an image or provide an Instagram link.", "Words: 0", "Translation failed: No input provided.", None
236
+
237
+ text, wc = extract_text_from_quote(source_image)
238
+ # Proceed only if text was found
239
+ if "No text" in text or "Error" in text:
240
+ return text, wc, "Translation failed: No text extracted.", None
241
+
242
+ translated = translate_extracted(text, lang)
243
+ final_image = overlay_text_on_image(translated)
244
+
245
+ return text, wc, translated, final_image
246
+
247
+ # The main button triggers the full pipeline
248
  extract_btn.click(
249
  fn=process_everything,
250
+ inputs=[image_input, ig_input, target_lang, gr.State(True)], # Pass True to force processing
251
  outputs=[text_output, word_count, translated_output, final_image_output]
252
  )
253
 
254
+ # Changing the image triggers the pipeline only if 'auto-translate' is checked
 
 
 
 
 
 
 
255
  image_input.change(
256
+ fn=process_everything,
257
+ inputs=[image_input, ig_input, target_lang, auto_translate],
258
  outputs=[text_output, word_count, translated_output, final_image_output]
259
  )
260
 
261
+ # Changing the IG link triggers the pipeline only if 'auto-translate' is checked
262
+ ig_input.change(
263
+ fn=process_everything,
264
+ inputs=[image_input, ig_input, target_lang, auto_translate],
265
+ outputs=[text_output, word_count, translated_output, final_image_output]
 
266
  )
267
 
268
+ # Clear button action
269
  clear_btn.click(
270
  fn=clear_all,
271
+ outputs=[image_input, ig_input, text_output, word_count, translated_output, final_image_output, auto_translate]
272
  )
273
 
274
+ gr.Markdown("### 💡 How It Works:\n1. Upload a clear image containing English text OR provide an Instagram post link.\n2. The app automatically extracts the text using OCR.\n3. The text is translated to a casual, modern Persian.\n4. The Persian text is sent to a second app which overlays it on a background image.\n5. The final image is displayed.")
275
 
276
  if __name__ == "__main__":
277
+ # Add requirements to your requirements.txt:
278
  # gradio
279
  # easyocr
280
  # pillow
 
282
  # google-generativeai
283
  # gradio_client
284
  # requests
285
+ demo.launch()