Update app.py
Browse files
app.py
CHANGED
@@ -6,6 +6,7 @@ from io import BytesIO
|
|
6 |
import base64
|
7 |
import os
|
8 |
import json
|
|
|
9 |
|
10 |
# Initialize the Google Generative AI client with the API key from environment variables
|
11 |
try:
|
@@ -14,38 +15,61 @@ except KeyError:
|
|
14 |
raise ValueError("Please set the GEMINI_API_KEY environment variable.")
|
15 |
client = genai.Client(api_key=api_key)
|
16 |
|
17 |
-
def generate_item(tag):
|
18 |
"""
|
19 |
-
Generate a single feed item
|
20 |
|
21 |
Args:
|
22 |
tag (str): The tag to base the content on.
|
|
|
23 |
|
24 |
Returns:
|
25 |
dict: A dictionary with 'text' (str) and 'image_base64' (str).
|
26 |
"""
|
27 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
28 |
prompt = f"""
|
29 |
Generate a short, engaging TikTok-style caption about {tag}.
|
30 |
Return the response as a JSON object with a single key 'caption' containing the caption text.
|
31 |
Example: {{"caption": "Craving this yummy treat! π #foodie"}}
|
32 |
Do not include additional commentary or options.
|
|
|
33 |
"""
|
34 |
text_response = client.models.generate_content(
|
35 |
model='gemini-2.5-flash-preview-04-17',
|
36 |
-
contents=[prompt]
|
|
|
37 |
)
|
38 |
# Parse JSON response to extract the caption
|
39 |
try:
|
40 |
response_json = json.loads(text_response.text.strip())
|
41 |
text = response_json['caption']
|
42 |
except (json.JSONDecodeError, KeyError):
|
43 |
-
text = f"
|
44 |
|
45 |
-
# Generate
|
46 |
image_prompt = f"""
|
47 |
-
A
|
48 |
-
The image should be
|
|
|
49 |
"""
|
50 |
image_response = client.models.generate_images(
|
51 |
model='imagen-3.0-generate-002',
|
@@ -63,7 +87,7 @@ def generate_item(tag):
|
|
63 |
image = Image.open(BytesIO(generated_image.image.image_bytes))
|
64 |
else:
|
65 |
# Fallback to a placeholder image
|
66 |
-
image = Image.new('RGB', (
|
67 |
|
68 |
# Convert the image to base64
|
69 |
buffered = BytesIO()
|
@@ -83,15 +107,15 @@ def start_feed(tag):
|
|
83 |
tuple: (current_tag, feed_items, html_content)
|
84 |
"""
|
85 |
if not tag.strip():
|
86 |
-
tag = "trending"
|
87 |
-
item = generate_item(tag)
|
88 |
feed_items = [item]
|
89 |
html_content = generate_html(feed_items)
|
90 |
return tag, feed_items, html_content
|
91 |
|
92 |
def load_more(current_tag, feed_items):
|
93 |
"""
|
94 |
-
Append a new item to the existing feed
|
95 |
|
96 |
Args:
|
97 |
current_tag (str): The tag currently being used for the feed.
|
@@ -100,23 +124,24 @@ def load_more(current_tag, feed_items):
|
|
100 |
Returns:
|
101 |
tuple: (current_tag, updated_feed_items, updated_html_content)
|
102 |
"""
|
103 |
-
new_item = generate_item(current_tag)
|
104 |
feed_items.append(new_item)
|
105 |
-
html_content = generate_html(feed_items)
|
106 |
return current_tag, feed_items, html_content
|
107 |
|
108 |
-
def generate_html(feed_items):
|
109 |
"""
|
110 |
-
Generate an HTML string to display the feed items in a TikTok-like
|
111 |
|
112 |
Args:
|
113 |
feed_items (list): List of dictionaries containing 'text' and 'image_base64'.
|
|
|
114 |
|
115 |
Returns:
|
116 |
str: HTML string representing the feed.
|
117 |
"""
|
118 |
html_str = """
|
119 |
-
<div style="
|
120 |
display: flex;
|
121 |
flex-direction: column;
|
122 |
align-items: center;
|
@@ -124,24 +149,28 @@ def generate_html(feed_items):
|
|
124 |
margin: 0 auto;
|
125 |
background-color: #000;
|
126 |
height: 640px;
|
127 |
-
overflow-y:
|
|
|
128 |
scrollbar-width: none;
|
129 |
-ms-overflow-style: none;
|
130 |
border: 1px solid #333;
|
131 |
border-radius: 10px;
|
132 |
">
|
133 |
"""
|
134 |
-
# Hide scrollbar
|
135 |
html_str += """
|
136 |
<style>
|
137 |
-
|
138 |
display: none;
|
139 |
}
|
|
|
|
|
|
|
140 |
</style>
|
141 |
"""
|
142 |
-
for item in feed_items:
|
143 |
html_str += f"""
|
144 |
-
<div style="
|
145 |
width: 100%;
|
146 |
height: 640px;
|
147 |
position: relative;
|
@@ -175,6 +204,15 @@ def generate_html(feed_items):
|
|
175 |
</div>
|
176 |
"""
|
177 |
html_str += "</div>"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
178 |
return html_str
|
179 |
|
180 |
# Define the Gradio interface
|
|
|
6 |
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:
|
|
|
15 |
raise ValueError("Please set the GEMINI_API_KEY environment variable.")
|
16 |
client = genai.Client(api_key=api_key)
|
17 |
|
18 |
+
def generate_item(tag, item_index):
|
19 |
"""
|
20 |
+
Generate a single feed item with diverse text and image.
|
21 |
|
22 |
Args:
|
23 |
tag (str): The tag to base the content on.
|
24 |
+
item_index (int): Index of the item to ensure diversity.
|
25 |
|
26 |
Returns:
|
27 |
dict: A dictionary with 'text' (str) and 'image_base64' (str).
|
28 |
"""
|
29 |
+
# Define varied styles for diversity in image generation
|
30 |
+
styles = [
|
31 |
+
"futuristic neon lighting",
|
32 |
+
"soft pastel tones with a dreamy vibe",
|
33 |
+
"vibrant and colorful pop art style",
|
34 |
+
"minimalist black and white aesthetic",
|
35 |
+
"retro 80s synthwave look",
|
36 |
+
"golden hour sunlight with warm tones"
|
37 |
+
]
|
38 |
+
perspectives = [
|
39 |
+
"a close-up view",
|
40 |
+
"a wide-angle shot",
|
41 |
+
"an aerial perspective",
|
42 |
+
"a side profile",
|
43 |
+
"a dynamic angled shot"
|
44 |
+
]
|
45 |
+
style = random.choice(styles)
|
46 |
+
perspective = random.choice(perspectives)
|
47 |
+
|
48 |
+
# Generate text with high temperature for diversity
|
49 |
prompt = f"""
|
50 |
Generate a short, engaging TikTok-style caption about {tag}.
|
51 |
Return the response as a JSON object with a single key 'caption' containing the caption text.
|
52 |
Example: {{"caption": "Craving this yummy treat! π #foodie"}}
|
53 |
Do not include additional commentary or options.
|
54 |
+
Use creative and varied language to ensure uniqueness.
|
55 |
"""
|
56 |
text_response = client.models.generate_content(
|
57 |
model='gemini-2.5-flash-preview-04-17',
|
58 |
+
contents=[prompt],
|
59 |
+
generation_config={"temperature": 1.2} # High temperature for diversity
|
60 |
)
|
61 |
# Parse JSON response to extract the caption
|
62 |
try:
|
63 |
response_json = json.loads(text_response.text.strip())
|
64 |
text = response_json['caption']
|
65 |
except (json.JSONDecodeError, KeyError):
|
66 |
+
text = f"Obsessed with {tag}! π₯ #{tag}" # Fallback caption
|
67 |
|
68 |
+
# Generate a diverse image based on the tag
|
69 |
image_prompt = f"""
|
70 |
+
A high-quality visual scene representing {tag}, designed for a TikTok video.
|
71 |
+
The image should be {perspective} with a {style}.
|
72 |
+
Ensure the image is colorful, engaging, and has no text or letters.
|
73 |
"""
|
74 |
image_response = client.models.generate_images(
|
75 |
model='imagen-3.0-generate-002',
|
|
|
87 |
image = Image.open(BytesIO(generated_image.image.image_bytes))
|
88 |
else:
|
89 |
# Fallback to a placeholder image
|
90 |
+
image = Image.new('RGB', (360, 640), color='gray') # 9:16 aspect ratio
|
91 |
|
92 |
# Convert the image to base64
|
93 |
buffered = BytesIO()
|
|
|
107 |
tuple: (current_tag, feed_items, html_content)
|
108 |
"""
|
109 |
if not tag.strip():
|
110 |
+
tag = "trending"
|
111 |
+
item = generate_item(tag, 0)
|
112 |
feed_items = [item]
|
113 |
html_content = generate_html(feed_items)
|
114 |
return tag, feed_items, html_content
|
115 |
|
116 |
def load_more(current_tag, feed_items):
|
117 |
"""
|
118 |
+
Append a new item to the existing feed and scroll to the latest item.
|
119 |
|
120 |
Args:
|
121 |
current_tag (str): The tag currently being used for the feed.
|
|
|
124 |
Returns:
|
125 |
tuple: (current_tag, updated_feed_items, updated_html_content)
|
126 |
"""
|
127 |
+
new_item = generate_item(current_tag, len(feed_items))
|
128 |
feed_items.append(new_item)
|
129 |
+
html_content = generate_html(feed_items, scroll_to_latest=True)
|
130 |
return current_tag, feed_items, html_content
|
131 |
|
132 |
+
def generate_html(feed_items, scroll_to_latest=False):
|
133 |
"""
|
134 |
+
Generate an HTML string to display the feed items in a TikTok-like carousel.
|
135 |
|
136 |
Args:
|
137 |
feed_items (list): List of dictionaries containing 'text' and 'image_base64'.
|
138 |
+
scroll_to_latest (bool): Whether to auto-scroll to the latest item.
|
139 |
|
140 |
Returns:
|
141 |
str: HTML string representing the feed.
|
142 |
"""
|
143 |
html_str = """
|
144 |
+
<div id="feed-container" style="
|
145 |
display: flex;
|
146 |
flex-direction: column;
|
147 |
align-items: center;
|
|
|
149 |
margin: 0 auto;
|
150 |
background-color: #000;
|
151 |
height: 640px;
|
152 |
+
overflow-y: scroll;
|
153 |
+
scroll-snap-type: y mandatory;
|
154 |
scrollbar-width: none;
|
155 |
-ms-overflow-style: none;
|
156 |
border: 1px solid #333;
|
157 |
border-radius: 10px;
|
158 |
">
|
159 |
"""
|
160 |
+
# Hide scrollbar
|
161 |
html_str += """
|
162 |
<style>
|
163 |
+
#feed-container::-webkit-scrollbar {
|
164 |
display: none;
|
165 |
}
|
166 |
+
.feed-item {
|
167 |
+
scroll-snap-align: start;
|
168 |
+
}
|
169 |
</style>
|
170 |
"""
|
171 |
+
for idx, item in enumerate(feed_items):
|
172 |
html_str += f"""
|
173 |
+
<div class="feed-item" id="item-{idx}" style="
|
174 |
width: 100%;
|
175 |
height: 640px;
|
176 |
position: relative;
|
|
|
204 |
</div>
|
205 |
"""
|
206 |
html_str += "</div>"
|
207 |
+
|
208 |
+
# Auto-scroll to the latest item if requested
|
209 |
+
if scroll_to_latest and feed_items:
|
210 |
+
html_str += f"""
|
211 |
+
<script>
|
212 |
+
document.getElementById('item-{len(feed_items) - 1}').scrollIntoView({{ behavior: 'smooth' }});
|
213 |
+
</script>
|
214 |
+
"""
|
215 |
+
|
216 |
return html_str
|
217 |
|
218 |
# Define the Gradio interface
|