Update app.py
Browse files
app.py
CHANGED
@@ -7,6 +7,7 @@ import base64
|
|
7 |
import os
|
8 |
import json
|
9 |
import random
|
|
|
10 |
|
11 |
# Initialize the Google Generative AI client with the API key from environment variables
|
12 |
try:
|
@@ -170,7 +171,7 @@ def start_feed(tag, current_index, feed_items):
|
|
170 |
feed_items (list): The current list of feed items.
|
171 |
|
172 |
Returns:
|
173 |
-
tuple: (current_tag, current_index, feed_items, html_content, is_loading)
|
174 |
"""
|
175 |
if not tag.strip():
|
176 |
tag = "trending"
|
@@ -178,12 +179,14 @@ def start_feed(tag, current_index, feed_items):
|
|
178 |
# Set loading state
|
179 |
is_loading = True
|
180 |
html_content = generate_html([], False, 0, tag, is_loading)
|
|
|
181 |
|
182 |
try:
|
183 |
ideas = generate_ideas(tag)
|
184 |
item = generate_item(tag, ideas)
|
185 |
feed_items = [item]
|
186 |
current_index = 0
|
|
|
187 |
except Exception as e:
|
188 |
print(f"Error in start_feed: {e}")
|
189 |
feed_items = []
|
@@ -207,12 +210,12 @@ def start_feed(tag, current_index, feed_items):
|
|
207 |
</div>
|
208 |
"""
|
209 |
is_loading = False
|
210 |
-
return tag, current_index, feed_items, html_content, is_loading
|
211 |
|
212 |
# Set loading state to False and update UI
|
213 |
is_loading = False
|
214 |
html_content = generate_html(feed_items, False, current_index, tag, is_loading)
|
215 |
-
return tag, current_index, feed_items, html_content, is_loading
|
216 |
|
217 |
def load_next(tag, current_index, feed_items):
|
218 |
"""
|
@@ -224,10 +227,11 @@ def load_next(tag, current_index, feed_items):
|
|
224 |
feed_items (list): The current list of feed items.
|
225 |
|
226 |
Returns:
|
227 |
-
tuple: (current_tag, current_index, feed_items, html_content, is_loading)
|
228 |
"""
|
229 |
is_loading = True
|
230 |
html_content = generate_html(feed_items, False, current_index, tag, is_loading)
|
|
|
231 |
|
232 |
try:
|
233 |
if current_index + 1 < len(feed_items):
|
@@ -237,6 +241,7 @@ def load_next(tag, current_index, feed_items):
|
|
237 |
new_item = generate_item(tag, ideas)
|
238 |
feed_items.append(new_item)
|
239 |
current_index = len(feed_items) - 1
|
|
|
240 |
except Exception as e:
|
241 |
print(f"Error in load_next: {e}")
|
242 |
html_content = """
|
@@ -258,11 +263,11 @@ def load_next(tag, current_index, feed_items):
|
|
258 |
</div>
|
259 |
"""
|
260 |
is_loading = False
|
261 |
-
return tag, current_index, feed_items, html_content, is_loading
|
262 |
|
263 |
is_loading = False
|
264 |
html_content = generate_html(feed_items, False, current_index, tag, is_loading)
|
265 |
-
return tag, current_index, feed_items, html_content, is_loading
|
266 |
|
267 |
def load_previous(tag, current_index, feed_items):
|
268 |
"""
|
@@ -274,16 +279,84 @@ def load_previous(tag, current_index, feed_items):
|
|
274 |
feed_items (list): The current list of feed items.
|
275 |
|
276 |
Returns:
|
277 |
-
tuple: (current_tag, current_index, feed_items, html_content, is_loading)
|
278 |
"""
|
279 |
if current_index > 0:
|
280 |
current_index -= 1
|
281 |
html_content = generate_html(feed_items, False, current_index, tag, False)
|
282 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
287 |
|
288 |
Args:
|
289 |
feed_items (list): List of dictionaries containing 'text' and 'image_base64'.
|
@@ -429,20 +502,6 @@ def generate_html(feed_items, scroll_to_latest=False, current_index=0, tag="", i
|
|
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>
|
@@ -456,7 +515,6 @@ def generate_html(feed_items, scroll_to_latest=False, current_index=0, tag="", i
|
|
456 |
}}
|
457 |
}}
|
458 |
</script>
|
459 |
-
<button id="next-button" style="display: none;" onclick="document.getElementById('next-button').click()"></button>
|
460 |
<button id="previous-button" style="display: none;" onclick="document.getElementById('previous-button').click()"></button>
|
461 |
""".format(image_base64=item['image_base64'], text=item['text'])
|
462 |
|
@@ -467,20 +525,24 @@ with gr.Blocks(
|
|
467 |
css="""
|
468 |
body { background-color: #000; color: #fff; font-family: Arial, sans-serif; }
|
469 |
.gradio-container { max-width: 400px; margin: 0 auto; padding: 10px; }
|
470 |
-
input, select { border-radius: 5px; background-color: #222; color: #fff; border: 1px solid #444; }
|
|
|
|
|
|
|
471 |
.gr-form { background-color: #111; padding: 15px; border-radius: 10px; }
|
472 |
""",
|
473 |
-
title="
|
474 |
) as demo:
|
475 |
# State variables
|
476 |
current_tag = gr.State(value="")
|
477 |
current_index = gr.State(value=0)
|
478 |
feed_items = gr.State(value=[])
|
479 |
is_loading = gr.State(value=False)
|
|
|
480 |
|
481 |
# Input section
|
482 |
with gr.Column(elem_classes="gr-form"):
|
483 |
-
gr.Markdown("### Create Your
|
484 |
with gr.Row():
|
485 |
suggested_tags = gr.Dropdown(
|
486 |
choices=["food", "travel", "fashion", "tech"],
|
@@ -493,9 +555,11 @@ with gr.Blocks(
|
|
493 |
placeholder="e.g., sushi, adventure",
|
494 |
submit_btn=False
|
495 |
)
|
|
|
496 |
|
497 |
# Output display
|
498 |
feed_html = gr.HTML()
|
|
|
499 |
|
500 |
# Event handlers
|
501 |
def set_tag(selected_tag):
|
@@ -510,32 +574,31 @@ with gr.Blocks(
|
|
510 |
).then(
|
511 |
fn=start_feed,
|
512 |
inputs=[tag_input, current_index, feed_items],
|
513 |
-
outputs=[current_tag, current_index, feed_items, feed_html, is_loading]
|
514 |
)
|
515 |
|
516 |
# Handle Enter keypress in the custom tag input
|
517 |
tag_input.submit(
|
518 |
fn=start_feed,
|
519 |
inputs=[tag_input, current_index, feed_items],
|
520 |
-
outputs=[current_tag, current_index, feed_items, feed_html, is_loading]
|
521 |
)
|
522 |
|
523 |
-
#
|
524 |
-
|
525 |
-
previous_button = gr.Button("Previous", elem_id="previous-button", visible=False)
|
526 |
-
|
527 |
-
# Handle click to go to next item
|
528 |
-
next_button.click(
|
529 |
fn=load_next,
|
530 |
inputs=[current_tag, current_index, feed_items],
|
531 |
-
outputs=[current_tag, current_index, feed_items, feed_html, is_loading]
|
532 |
)
|
533 |
|
|
|
|
|
|
|
534 |
# Handle click to go to previous item
|
535 |
previous_button.click(
|
536 |
fn=load_previous,
|
537 |
inputs=[current_tag, current_index, feed_items],
|
538 |
-
outputs=[current_tag, current_index, feed_items, feed_html, is_loading]
|
539 |
)
|
540 |
|
541 |
# Launch the app
|
|
|
7 |
import os
|
8 |
import json
|
9 |
import random
|
10 |
+
import urllib.parse
|
11 |
|
12 |
# Initialize the Google Generative AI client with the API key from environment variables
|
13 |
try:
|
|
|
171 |
feed_items (list): The current list of feed items.
|
172 |
|
173 |
Returns:
|
174 |
+
tuple: (current_tag, current_index, feed_items, html_content, share_links, is_loading)
|
175 |
"""
|
176 |
if not tag.strip():
|
177 |
tag = "trending"
|
|
|
179 |
# Set loading state
|
180 |
is_loading = True
|
181 |
html_content = generate_html([], False, 0, tag, is_loading)
|
182 |
+
share_links = ""
|
183 |
|
184 |
try:
|
185 |
ideas = generate_ideas(tag)
|
186 |
item = generate_item(tag, ideas)
|
187 |
feed_items = [item]
|
188 |
current_index = 0
|
189 |
+
share_links = generate_share_links(item['image_base64'], item['text'])
|
190 |
except Exception as e:
|
191 |
print(f"Error in start_feed: {e}")
|
192 |
feed_items = []
|
|
|
210 |
</div>
|
211 |
"""
|
212 |
is_loading = False
|
213 |
+
return tag, current_index, feed_items, html_content, share_links, is_loading
|
214 |
|
215 |
# Set loading state to False and update UI
|
216 |
is_loading = False
|
217 |
html_content = generate_html(feed_items, False, current_index, tag, is_loading)
|
218 |
+
return tag, current_index, feed_items, html_content, share_links, is_loading
|
219 |
|
220 |
def load_next(tag, current_index, feed_items):
|
221 |
"""
|
|
|
227 |
feed_items (list): The current list of feed items.
|
228 |
|
229 |
Returns:
|
230 |
+
tuple: (current_tag, current_index, feed_items, html_content, share_links, is_loading)
|
231 |
"""
|
232 |
is_loading = True
|
233 |
html_content = generate_html(feed_items, False, current_index, tag, is_loading)
|
234 |
+
share_links = ""
|
235 |
|
236 |
try:
|
237 |
if current_index + 1 < len(feed_items):
|
|
|
241 |
new_item = generate_item(tag, ideas)
|
242 |
feed_items.append(new_item)
|
243 |
current_index = len(feed_items) - 1
|
244 |
+
share_links = generate_share_links(feed_items[current_index]['image_base64'], feed_items[current_index]['text'])
|
245 |
except Exception as e:
|
246 |
print(f"Error in load_next: {e}")
|
247 |
html_content = """
|
|
|
263 |
</div>
|
264 |
"""
|
265 |
is_loading = False
|
266 |
+
return tag, current_index, feed_items, html_content, share_links, is_loading
|
267 |
|
268 |
is_loading = False
|
269 |
html_content = generate_html(feed_items, False, current_index, tag, is_loading)
|
270 |
+
return tag, current_index, feed_items, html_content, share_links, is_loading
|
271 |
|
272 |
def load_previous(tag, current_index, feed_items):
|
273 |
"""
|
|
|
279 |
feed_items (list): The current list of feed items.
|
280 |
|
281 |
Returns:
|
282 |
+
tuple: (current_tag, current_index, feed_items, html_content, share_links, is_loading)
|
283 |
"""
|
284 |
if current_index > 0:
|
285 |
current_index -= 1
|
286 |
html_content = generate_html(feed_items, False, current_index, tag, False)
|
287 |
+
share_links = generate_share_links(feed_items[current_index]['image_base64'], feed_items[current_index]['text'])
|
288 |
+
return tag, current_index, feed_items, html_content, share_links, False
|
289 |
+
|
290 |
+
def generate_share_links(image_base64, caption):
|
291 |
+
"""
|
292 |
+
Generate share links for social media platforms.
|
293 |
+
|
294 |
+
Args:
|
295 |
+
image_base64 (str): The base64-encoded image data.
|
296 |
+
caption (str): The caption to share with the image.
|
297 |
+
|
298 |
+
Returns:
|
299 |
+
str: HTML string with share links.
|
300 |
+
"""
|
301 |
+
# Create a data URL for the image
|
302 |
+
image_data_url = f"data:image/png;base64,{image_base64}"
|
303 |
+
encoded_caption = urllib.parse.quote(caption)
|
304 |
+
encoded_image_url = urllib.parse.quote(image_data_url)
|
305 |
+
|
306 |
+
# Generate share links for each platform
|
307 |
+
share_links = """
|
308 |
+
<div style="
|
309 |
+
display: flex;
|
310 |
+
flex-wrap: wrap;
|
311 |
+
justify-content: center;
|
312 |
+
gap: 10px;
|
313 |
+
margin-top: 10px;
|
314 |
+
color: white;
|
315 |
+
font-family: Arial, sans-serif;
|
316 |
+
">
|
317 |
+
<a href="https://www.tiktok.com/upload?caption={caption}" target="_blank" style="
|
318 |
+
background-color: #00f2ea;
|
319 |
+
color: #000;
|
320 |
+
padding: 5px 10px;
|
321 |
+
border-radius: 5px;
|
322 |
+
text-decoration: none;
|
323 |
+
">Share on TikTok</a>
|
324 |
+
<a href="https://www.instagram.com/?url={image_url}&caption={caption}" target="_blank" style="
|
325 |
+
background-color: #e1306c;
|
326 |
+
color: white;
|
327 |
+
padding: 5px 10px;
|
328 |
+
border-radius: 5px;
|
329 |
+
text-decoration: none;
|
330 |
+
">Share on Instagram</a>
|
331 |
+
<a href="https://www.facebook.com/sharer/sharer.php?u={image_url}"e={caption}" target="_blank" style="
|
332 |
+
background-color: #4267b2;
|
333 |
+
color: white;
|
334 |
+
padding: 5px 10px;
|
335 |
+
border-radius: 5px;
|
336 |
+
text-decoration: none;
|
337 |
+
">Share on Facebook</a>
|
338 |
+
<a href="https://twitter.com/intent/tweet?text={caption}&url={image_url}" target="_blank" style="
|
339 |
+
background-color: #1da1f2;
|
340 |
+
color: white;
|
341 |
+
padding: 5px 10px;
|
342 |
+
border-radius: 5px;
|
343 |
+
text-decoration: none;
|
344 |
+
">Share on X</a>
|
345 |
+
<a href="https://pinterest.com/pin/create/button/?url={image_url}&description={caption}" target="_blank" style="
|
346 |
+
background-color: #bd081c;
|
347 |
+
color: white;
|
348 |
+
padding: 5px 10px;
|
349 |
+
border-radius: 5px;
|
350 |
+
text-decoration: none;
|
351 |
+
">Share on Pinterest</a>
|
352 |
+
</div>
|
353 |
+
""".format(caption=encoded_caption, image_url=encoded_image_url)
|
354 |
+
|
355 |
+
return share_links
|
356 |
|
357 |
def generate_html(feed_items, scroll_to_latest=False, current_index=0, tag="", is_loading=False):
|
358 |
"""
|
359 |
+
Generate an HTML string to display the current feed item with click navigation.
|
360 |
|
361 |
Args:
|
362 |
feed_items (list): List of dictionaries containing 'text' and 'image_base64'.
|
|
|
502 |
">
|
503 |
{text}
|
504 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
505 |
</div>
|
506 |
</div>
|
507 |
<script>
|
|
|
515 |
}}
|
516 |
}}
|
517 |
</script>
|
|
|
518 |
<button id="previous-button" style="display: none;" onclick="document.getElementById('previous-button').click()"></button>
|
519 |
""".format(image_base64=item['image_base64'], text=item['text'])
|
520 |
|
|
|
525 |
css="""
|
526 |
body { background-color: #000; color: #fff; font-family: Arial, sans-serif; }
|
527 |
.gradio-container { max-width: 400px; margin: 0 auto; padding: 10px; }
|
528 |
+
input, select, button { border-radius: 5px; background-color: #222; color: #fff; border: 1px solid #444; }
|
529 |
+
button { background-color: #ff2d55; border: none; }
|
530 |
+
button:hover { background-color: #e0264b; }
|
531 |
+
.gr-button { width: 100%; margin-top: 10px; }
|
532 |
.gr-form { background-color: #111; padding: 15px; border-radius: 10px; }
|
533 |
""",
|
534 |
+
title="Create Your Feed" # Updated title
|
535 |
) as demo:
|
536 |
# State variables
|
537 |
current_tag = gr.State(value="")
|
538 |
current_index = gr.State(value=0)
|
539 |
feed_items = gr.State(value=[])
|
540 |
is_loading = gr.State(value=False)
|
541 |
+
share_links = gr.State(value="")
|
542 |
|
543 |
# Input section
|
544 |
with gr.Column(elem_classes="gr-form"):
|
545 |
+
gr.Markdown("### Create Your Feed")
|
546 |
with gr.Row():
|
547 |
suggested_tags = gr.Dropdown(
|
548 |
choices=["food", "travel", "fashion", "tech"],
|
|
|
555 |
placeholder="e.g., sushi, adventure",
|
556 |
submit_btn=False
|
557 |
)
|
558 |
+
magic_button = gr.Button("✨ Generate Next Item", elem_classes="gr-button")
|
559 |
|
560 |
# Output display
|
561 |
feed_html = gr.HTML()
|
562 |
+
share_html = gr.HTML(label="Share this item:")
|
563 |
|
564 |
# Event handlers
|
565 |
def set_tag(selected_tag):
|
|
|
574 |
).then(
|
575 |
fn=start_feed,
|
576 |
inputs=[tag_input, current_index, feed_items],
|
577 |
+
outputs=[current_tag, current_index, feed_items, feed_html, share_html, is_loading]
|
578 |
)
|
579 |
|
580 |
# Handle Enter keypress in the custom tag input
|
581 |
tag_input.submit(
|
582 |
fn=start_feed,
|
583 |
inputs=[tag_input, current_index, feed_items],
|
584 |
+
outputs=[current_tag, current_index, feed_items, feed_html, share_html, is_loading]
|
585 |
)
|
586 |
|
587 |
+
# Handle magic button click to generate next item
|
588 |
+
magic_button.click(
|
|
|
|
|
|
|
|
|
589 |
fn=load_next,
|
590 |
inputs=[current_tag, current_index, feed_items],
|
591 |
+
outputs=[current_tag, current_index, feed_items, feed_html, share_html, is_loading]
|
592 |
)
|
593 |
|
594 |
+
# Hidden button for previous item navigation
|
595 |
+
previous_button = gr.Button("Previous", elem_id="previous-button", visible=False)
|
596 |
+
|
597 |
# Handle click to go to previous item
|
598 |
previous_button.click(
|
599 |
fn=load_previous,
|
600 |
inputs=[current_tag, current_index, feed_items],
|
601 |
+
outputs=[current_tag, current_index, feed_items, feed_html, share_html, is_loading]
|
602 |
)
|
603 |
|
604 |
# Launch the app
|