codelion commited on
Commit
35126fc
Β·
verified Β·
1 Parent(s): 89c081e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +92 -62
app.py CHANGED
@@ -25,7 +25,6 @@ def clean_response_text(response_text):
25
  Returns:
26
  str: The cleaned response text.
27
  """
28
- # Remove ```json and ``` markers
29
  cleaned_text = response_text.strip()
30
  if cleaned_text.startswith("```json"):
31
  cleaned_text = cleaned_text[len("```json"):].strip()
@@ -59,7 +58,6 @@ def generate_ideas(tag):
59
  print(f"Raw response for ideas: {response.text}") # Debugging
60
  if not response.text or response.text.isspace():
61
  raise ValueError("Empty response from API")
62
- # Clean the response text
63
  cleaned_text = clean_response_text(response.text)
64
  response_json = json.loads(cleaned_text)
65
  if 'ideas' not in response_json or not isinstance(response_json['ideas'], list):
@@ -75,72 +73,92 @@ def generate_ideas(tag):
75
  f"An action-packed {tag} scene with dynamic colors"
76
  ]
77
 
78
- def generate_item(tag, ideas):
79
  """
80
- Generate a single feed item using one of the ideas.
81
 
82
  Args:
83
  tag (str): The tag to base the content on.
84
  ideas (list): List of ideas to choose from.
 
85
 
86
  Returns:
87
- dict: A dictionary with 'text' (str) and 'image_base64' (str).
88
  """
89
- selected_idea = random.choice(ideas)
90
- prompt = f"""
91
- Based on the idea "{selected_idea}", create content for a TikTok video about {tag}.
92
- Return a JSON object with two keys:
93
- - 'caption': A short, viral TikTok-style caption with hashtags.
94
- - 'image_prompt': A detailed image prompt for generating a high-quality visual scene.
95
- The image prompt should describe the scene vividly, specify a perspective and style, and ensure no text or letters are included.
96
- Ensure the response is strictly in JSON format.
97
- Example: {{"caption": "Neon vibes only! 🌌 #tech", "image_prompt": "A close-up view of a neon-lit gaming setup with RGB lights flashing, in a futuristic style, no text or letters"}}
98
- """
99
- try:
100
- response = client.models.generate_content(
101
- model='gemini-2.0-flash',
102
- contents=[prompt],
103
- config=types.GenerateContentConfig(temperature=1.2)
104
- )
105
- print(f"Raw response for item: {response.text}") # Debugging
106
- if not response.text or response.text.isspace():
107
- raise ValueError("Empty response from API")
108
- # Clean the response text
109
- cleaned_text = clean_response_text(response.text)
110
- response_json = json.loads(cleaned_text)
111
- if 'caption' not in response_json or 'image_prompt' not in response_json:
112
- raise ValueError("Invalid JSON format: 'caption' or 'image_prompt' key missing")
113
- text = response_json['caption']
114
- image_prompt = response_json['image_prompt']
115
- except Exception as e:
116
- print(f"Error generating item: {e}")
117
- text = f"Obsessed with {tag}! πŸ”₯ #{tag}"
118
- image_prompt = f"A vivid scene of {selected_idea}, in a vibrant pop art style, no text or letters"
119
-
120
- try:
121
- image_response = client.models.generate_images(
122
- model='imagen-3.0-generate-002',
123
- prompt=image_prompt,
124
- config=types.GenerateImagesConfig(
125
- number_of_images=1,
126
- aspect_ratio="9:16",
127
- person_generation="DONT_ALLOW"
128
  )
129
- )
130
- if image_response.generated_images and len(image_response.generated_images) > 0:
131
- generated_image = image_response.generated_images[0]
132
- image = Image.open(BytesIO(generated_image.image.image_bytes))
133
- else:
134
- image = Image.new('RGB', (360, 640), color='gray')
135
- except Exception as e:
136
- print(f"Error generating image: {e}")
137
- image = Image.new('RGB', (360, 640), color='gray')
138
-
139
- buffered = BytesIO()
140
- image.save(buffered, format="PNG")
141
- img_str = base64.b64encode(buffered.getvalue()).decode()
142
 
143
- return {'text': text, 'image_base64': img_str, 'ideas': ideas}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
144
 
145
  def start_feed(tag, current_index, feed_items):
146
  """
@@ -265,7 +283,7 @@ def load_previous(tag, current_index, feed_items):
265
 
266
  def generate_html(feed_items, scroll_to_latest=False, current_index=0, tag="", is_loading=False):
267
  """
268
- Generate an HTML string to display the current feed item with click navigation.
269
 
270
  Args:
271
  feed_items (list): List of dictionaries containing 'text' and 'image_base64'.
@@ -411,6 +429,20 @@ def generate_html(feed_items, scroll_to_latest=False, current_index=0, tag="", i
411
  ">
412
  {text}
413
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
414
  </div>
415
  </div>
416
  <script>
@@ -421,8 +453,6 @@ def generate_html(feed_items, scroll_to_latest=False, current_index=0, tag="", i
421
  const width = rect.width;
422
  if (clickX > width * 0.75) {{
423
  document.getElementById('previous-button').click();
424
- }} else {{
425
- document.getElementById('next-button').click();
426
  }}
427
  }}
428
  </script>
@@ -508,5 +538,5 @@ with gr.Blocks(
508
  outputs=[current_tag, current_index, feed_items, feed_html, is_loading]
509
  )
510
 
511
- # Launch the app (removed share=True since we're on Hugging Face Spaces)
512
  demo.launch()
 
25
  Returns:
26
  str: The cleaned response text.
27
  """
 
28
  cleaned_text = response_text.strip()
29
  if cleaned_text.startswith("```json"):
30
  cleaned_text = cleaned_text[len("```json"):].strip()
 
58
  print(f"Raw response for ideas: {response.text}") # Debugging
59
  if not response.text or response.text.isspace():
60
  raise ValueError("Empty response from API")
 
61
  cleaned_text = clean_response_text(response.text)
62
  response_json = json.loads(cleaned_text)
63
  if 'ideas' not in response_json or not isinstance(response_json['ideas'], list):
 
73
  f"An action-packed {tag} scene with dynamic colors"
74
  ]
75
 
76
+ def generate_item(tag, ideas, max_retries=3):
77
  """
78
+ Generate a single feed item using one of the ideas, retrying if image generation fails.
79
 
80
  Args:
81
  tag (str): The tag to base the content on.
82
  ideas (list): List of ideas to choose from.
83
+ max_retries (int): Maximum number of retries if image generation fails.
84
 
85
  Returns:
86
+ dict: A dictionary with 'text' (str), 'image_base64' (str), and 'ideas' (list).
87
  """
88
+ for attempt in range(max_retries):
89
+ selected_idea = random.choice(ideas)
90
+ prompt = f"""
91
+ Based on the idea "{selected_idea}", create content for a TikTok video about {tag}.
92
+ Return a JSON object with two keys:
93
+ - 'caption': A short, viral TikTok-style caption with hashtags.
94
+ - 'image_prompt': A detailed image prompt for generating a high-quality visual scene.
95
+ The image prompt should describe the scene vividly, specify a perspective and style, and ensure no text or letters are included.
96
+ Ensure the response is strictly in JSON format.
97
+ Example: {{"caption": "Neon vibes only! 🌌 #tech", "image_prompt": "A close-up view of a neon-lit gaming setup with RGB lights flashing, in a futuristic style, no text or letters"}}
98
+ """
99
+ try:
100
+ response = client.models.generate_content(
101
+ model='gemini-2.0-flash',
102
+ contents=[prompt],
103
+ config=types.GenerateContentConfig(temperature=1.2)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
  )
105
+ print(f"Raw response for item (attempt {attempt + 1}): {response.text}") # Debugging
106
+ if not response.text or response.text.isspace():
107
+ raise ValueError("Empty response from API")
108
+ cleaned_text = clean_response_text(response.text)
109
+ response_json = json.loads(cleaned_text)
110
+ if 'caption' not in response_json or 'image_prompt' not in response_json:
111
+ raise ValueError("Invalid JSON format: 'caption' or 'image_prompt' key missing")
112
+ text = response_json['caption']
113
+ image_prompt = response_json['image_prompt']
114
+ except Exception as e:
115
+ print(f"Error generating item (attempt {attempt + 1}): {e}")
116
+ text = f"Obsessed with {tag}! πŸ”₯ #{tag}"
117
+ image_prompt = f"A vivid scene of {selected_idea}, in a vibrant pop art style, no text or letters"
118
 
119
+ # Attempt to generate the image
120
+ try:
121
+ image_response = client.models.generate_images(
122
+ model='imagen-3.0-generate-002',
123
+ prompt=image_prompt,
124
+ config=types.GenerateImagesConfig(
125
+ number_of_images=1,
126
+ aspect_ratio="9:16",
127
+ person_generation="DONT_ALLOW"
128
+ )
129
+ )
130
+ if image_response.generated_images and len(image_response.generated_images) > 0:
131
+ generated_image = image_response.generated_images[0]
132
+ image = Image.open(BytesIO(generated_image.image.image_bytes))
133
+ # Successfully generated an image, proceed
134
+ buffered = BytesIO()
135
+ image.save(buffered, format="PNG")
136
+ img_str = base64.b64encode(buffered.getvalue()).decode()
137
+ return {'text': text, 'image_base64': img_str, 'ideas': ideas}
138
+ else:
139
+ print(f"Image generation failed (attempt {attempt + 1}): No images returned")
140
+ if attempt == max_retries - 1:
141
+ # Last attempt, use a gray placeholder
142
+ image = Image.new('RGB', (360, 640), color='gray')
143
+ buffered = BytesIO()
144
+ image.save(buffered, format="PNG")
145
+ img_str = base64.b64encode(buffered.getvalue()).decode()
146
+ return {'text': text, 'image_base64': img_str, 'ideas': ideas}
147
+ # Retry with new ideas
148
+ ideas = generate_ideas(tag)
149
+ continue
150
+ except Exception as e:
151
+ print(f"Error generating image (attempt {attempt + 1}): {e}")
152
+ if attempt == max_retries - 1:
153
+ # Last attempt, use a gray placeholder
154
+ image = Image.new('RGB', (360, 640), color='gray')
155
+ buffered = BytesIO()
156
+ image.save(buffered, format="PNG")
157
+ img_str = base64.b64encode(buffered.getvalue()).decode()
158
+ return {'text': text, 'image_base64': img_str, 'ideas': ideas}
159
+ # Retry with new ideas
160
+ ideas = generate_ideas(tag)
161
+ continue
162
 
163
  def start_feed(tag, current_index, feed_items):
164
  """
 
283
 
284
  def generate_html(feed_items, scroll_to_latest=False, current_index=0, tag="", is_loading=False):
285
  """
286
+ Generate an HTML string to display the current feed item with click navigation and a next button.
287
 
288
  Args:
289
  feed_items (list): List of dictionaries containing 'text' and 'image_base64'.
 
429
  ">
430
  {text}
431
  </div>
432
+ <button id="next-button" style="
433
+ position: absolute;
434
+ bottom: 80px;
435
+ right: 20px;
436
+ background-color: #ff2d55;
437
+ color: white;
438
+ border: none;
439
+ border-radius: 50%;
440
+ width: 50px;
441
+ height: 50px;
442
+ font-size: 24px;
443
+ cursor: pointer;
444
+ z-index: 3;
445
+ " onclick="document.getElementById('next-button').click()">✨</button>
446
  </div>
447
  </div>
448
  <script>
 
453
  const width = rect.width;
454
  if (clickX > width * 0.75) {{
455
  document.getElementById('previous-button').click();
 
 
456
  }}
457
  }}
458
  </script>
 
538
  outputs=[current_tag, current_index, feed_items, feed_html, is_loading]
539
  )
540
 
541
+ # Launch the app
542
  demo.launch()