codelion commited on
Commit
83b50de
Β·
verified Β·
1 Parent(s): b5e0ef9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +216 -180
app.py CHANGED
@@ -1,11 +1,7 @@
1
  import gradio as gr
2
- from google import genai
3
- from google.genai import types
4
  from PIL import Image
5
  from io import BytesIO
6
  import base64
7
- import os
8
- import json
9
  import random
10
  import urllib.parse
11
  import time
@@ -16,22 +12,6 @@ current_version = gr.__version__
16
  if current_version < required_version:
17
  raise ValueError(f"Gradio version {current_version} is outdated. Please upgrade to {required_version} or later using 'pip install gradio=={required_version}'.")
18
 
19
- # Initialize the Google Generative AI client
20
- try:
21
- api_key = os.environ['GEMINI_API_KEY']
22
- except KeyError:
23
- raise ValueError("Please set the GEMINI_API_KEY environment variable.")
24
- client = genai.Client(api_key=api_key)
25
-
26
- # Safety settings to disable all filters
27
- SAFETY_SETTINGS = [
28
- types.SafetySetting(category=types.HarmCategory.HARM_CATEGORY_HARASSMENT, threshold=types.HarmBlockThreshold.BLOCK_NONE),
29
- types.SafetySetting(category=types.HarmCategory.HARM_CATEGORY_HATE_SPEECH, threshold=types.HarmBlockThreshold.BLOCK_NONE),
30
- types.SafetySetting(category=types.HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT, threshold=types.HarmBlockThreshold.BLOCK_NONE),
31
- types.SafetySetting(category=types.HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT, threshold=types.HarmBlockThreshold.BLOCK_NONE),
32
- types.SafetySetting(category=types.HarmCategory.HARM_CATEGORY_CIVIC_INTEGRITY, threshold=types.HarmBlockThreshold.BLOCK_NONE),
33
- ]
34
-
35
  def clean_response_text(response_text):
36
  """Clean API response by removing Markdown code block markers."""
37
  cleaned_text = response_text.strip()
@@ -44,132 +24,66 @@ def clean_response_text(response_text):
44
  def generate_ideas(user_input):
45
  """Generate 5 creative TikTok video ideas based on user input."""
46
  yield (10, f"Brainstorming epic ideas for {user_input}... 🌟")
47
-
48
- prompt = f"""
49
- The user has provided the concept: "{user_input}". Generate 5 diverse and creative ideas for a TikTok video explicitly related to "{user_input}".
50
- Each idea should be a short sentence describing a specific scene or concept.
51
- Return the response as a JSON object with a key 'ideas' containing a list of 5 ideas.
52
- Example for "blindfolded Rubik's Cube challenge":
53
- {{"ideas": [
54
- "A blindfolded speedcubing competition with dramatic music",
55
- "A close-up of a person solving a Rubik's Cube blindfolded under a spotlight",
56
- "A time-lapse of a blindfolded Rubik's Cube solve with colorful lighting",
57
- "A blindfolded Rubik's Cube challenge in a futuristic setting",
58
- "A split-screen of two people racing to solve a Rubik's Cube blindfolded"
59
- ]}}
60
- """
61
  try:
62
- response = client.models.generate_content(
63
- model='gemini-2.0-flash-lite',
64
- contents=[prompt],
65
- config=types.GenerateContentConfig(temperature=1.2, safety_settings=SAFETY_SETTINGS)
66
- )
67
- cleaned_text = clean_response_text(response.text)
68
- response_json = json.loads(cleaned_text)
69
- if 'ideas' not in response_json or len(response_json['ideas']) != 5:
70
- raise ValueError("Invalid JSON format or incorrect number of ideas")
71
- ideas = response_json['ideas']
72
  yield (20, f"Ideas locked in for {user_input}! πŸš€")
73
- return ideas
74
  except Exception as e:
75
  print(f"Error generating ideas: {e}")
76
  yield (20, f"Oops, tweaking the plan for {user_input}... πŸ”§")
77
- return [
78
  f"A dramatic {user_input} scene with cinematic lighting",
79
  f"A close-up of {user_input} in a futuristic setting",
80
- f"A high-energy {user_input} moment with vibrant colors",
81
- f"A serene {user_input} scene with soft focus",
82
- f"An action-packed {user_input} challenge with dynamic angles"
83
  ]
 
84
 
85
  def generate_item(user_input, ideas, generate_video=False):
86
  """Generate a feed item (image and optionally video) with progress updates."""
87
- video_base64 = None
88
- max_attempts = 3
89
-
90
- total_attempts = 0
91
- while total_attempts < max_attempts:
92
- total_attempts += 1
93
- yield (20 + total_attempts * 10, f"Attempt {total_attempts} to craft your {user_input} masterpiece... 🎨")
94
-
95
- selected_idea = random.choice(ideas)
96
- prompt = f"""
97
- The user has provided the concept: "{user_input}". Based on this and the idea "{selected_idea}", create content for a TikTok video.
98
- Return a JSON object with:
99
- - 'caption': A short, viral TikTok-style caption with hashtags reflecting "{user_input}".
100
- - 'image_prompt': A detailed image prompt for a high-quality visual scene, no text or letters.
101
- Example: {{"caption": "Blindfolded Rubik's Cube MAGIC! 🀯 #rubiks", "image_prompt": "A close-up view of a person solving a Rubik's Cube blindfolded, dramatic style"}}
102
- """
103
- try:
104
- response = client.models.generate_content(
105
- model='gemini-2.0-flash-lite',
106
- contents=[prompt],
107
- config=types.GenerateContentConfig(temperature=1.2, safety_settings=SAFETY_SETTINGS)
108
- )
109
- cleaned_text = clean_response_text(response.text)
110
- response_json = json.loads(cleaned_text)
111
- text = response_json['caption']
112
- image_prompt = response_json['image_prompt']
113
- except Exception as e:
114
- print(f"Error generating item: {e}")
115
- text = f"Amazing {user_input}! πŸ”₯ #{user_input.replace(' ', '')}"
116
- image_prompt = f"A vivid scene of {selected_idea} related to {user_input}, vibrant pop art style, no text"
117
-
118
- # Generate image
119
- try:
120
- yield (40, f"Rendering your {user_input} vision... ✨")
121
- imagen = client.models.generate_images(
122
- model='imagen-3.0-generate-002',
123
- prompt=image_prompt,
124
- config=types.GenerateImagesConfig(aspect_ratio="9:16", number_of_images=1)
125
- )
126
- if imagen.generated_images:
127
- image = Image.open(BytesIO(imagen.generated_images[0].image.image_bytes))
128
- image = image.resize((360, 640), Image.LANCZOS)
129
- buffered = BytesIO()
130
- image.save(buffered, format="PNG")
131
- img_str = base64.b64encode(buffered.getvalue()).decode()
132
- yield (50, f"Image for {user_input} is ready! πŸŽ‰")
133
- break
134
- except Exception as e:
135
- print(f"Error generating image: {e}")
136
- if total_attempts == max_attempts:
137
- image = Image.new('RGB', (360, 640), color='gray')
138
- buffered = BytesIO()
139
- image.save(buffered, format="PNG")
140
- img_str = base64.b64encode(buffered.getvalue()).decode()
141
- yield (60, f"Using a placeholder for {user_input}... πŸ–ΌοΈ")
142
- return {'text': text, 'image_base64': img_str, 'video_base64': None, 'ideas': ideas}
143
- continue
144
-
145
- # Generate video if requested
146
  if generate_video:
147
- try:
148
- yield (60, f"Filming a viral video for {user_input}... πŸŽ₯")
149
- operation = client.models.generate_videos(
150
- model="veo-2.0-generate-001",
151
- prompt=f"A close-up slow dolly shot of {image_prompt}, realistic style",
152
- image=imagen.generated_images[0].image,
153
- config=types.GenerateVideosConfig(aspect_ratio="9:16", duration_seconds=8)
154
- )
155
- while not operation.done:
156
- time.sleep(20)
157
- operation = client.operations.get(operation)
158
- if operation.response and operation.response.generated_videos:
159
- video_data = client.files.download(file=operation.response.generated_videos[0].video)
160
- video_bytes = video_data if isinstance(video_data, bytes) else BytesIO(video_data).getvalue()
161
- video_base64 = base64.b64encode(video_bytes).decode()
162
- yield (90, f"Video for {user_input} is a wrap! 🎬")
163
- return {'text': text, 'image_base64': img_str, 'video_base64': video_base64, 'ideas': ideas}
164
- except Exception as e:
165
- print(f"Error generating video: {e}")
166
- yield (70, f"Skipping video for {user_input}... πŸ“Œ")
167
-
168
  yield (95, f"Polishing your {user_input} masterpiece... ✨")
169
- return {'text': text, 'image_base64': img_str, 'video_base64': video_base64, 'ideas': ideas}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
 
171
  def generate_progress_html(progress, message, user_input):
172
- """Generate HTML for the progress bar."""
173
  return f"""
174
  <div id="progress-container" style="
175
  display: flex; flex-direction: column; align-items: center; justify-content: center;
@@ -189,28 +103,106 @@ def generate_progress_html(progress, message, user_input):
189
  </div>
190
  """
191
 
192
- def generate_html(feed_items, manual_upload, current_index, user_input, is_loading):
193
  """Generate HTML for the feed display."""
 
 
 
 
 
 
 
 
194
  if not feed_items or current_index >= len(feed_items):
195
- return "<div style='color: white; text-align: center;'>No content yet!</div>"
 
 
 
 
196
  item = feed_items[current_index]
197
- media_html = f'<img src="data:image/png;base64,{item["image_base64"]}" style="width: 360px; height: 640px; object-fit: cover;" />'
 
 
 
198
  if item['video_base64']:
199
- media_html += f'<video controls src="data:video/mp4;base64,{item["video_base64"]}" style="width: 360px; height: 640px;"></video>'
 
 
 
 
 
 
200
  return f"""
201
- <div style="max-width: 360px; margin: 0 auto; background-color: #000; border: 1px solid #333; border-radius: 10px; color: white;">
202
- {media_html}
203
- <p style="padding: 10px;">{item['text']}</p>
 
 
 
 
 
 
 
 
 
 
 
 
204
  </div>
 
 
 
 
 
 
 
 
 
 
 
205
  """
206
 
207
- def generate_share_links(image_base64, video_base64, text):
208
- """Generate YouTube share links."""
209
- share_text = urllib.parse.quote(f"{text} Check out this cool content!")
210
- return f'<a href="https://www.youtube.com/upload?description={share_text}" target="_blank">Share on YouTube</a>'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
211
 
212
  def start_feed(user_input, generate_video, current_index, feed_items):
213
- """Start or reset the feed with a new item."""
214
  user_input = user_input.strip() or "trending"
215
  current_user_input = user_input
216
  is_loading = True
@@ -226,8 +218,11 @@ def start_feed(user_input, generate_video, current_index, feed_items):
226
  progress_html = generate_progress_html(progress, message, user_input)
227
  yield (gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), is_loading, progress_html)
228
  else:
229
- ideas = update
230
-
 
 
 
231
  item_gen = generate_item(user_input, ideas, generate_video)
232
  item = None
233
  for update in item_gen:
@@ -236,8 +231,11 @@ def start_feed(user_input, generate_video, current_index, feed_items):
236
  progress_html = generate_progress_html(progress, message, user_input)
237
  yield (gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), is_loading, progress_html)
238
  else:
239
- item = update
240
-
 
 
 
241
  feed_items = [item]
242
  current_index = 0
243
  feed_html = generate_html(feed_items, False, current_index, user_input, is_loading=False)
@@ -246,49 +244,59 @@ def start_feed(user_input, generate_video, current_index, feed_items):
246
  except Exception as e:
247
  print(f"Error in start_feed: {e}")
248
  feed_html = "<div style='color: white; text-align: center;'>Error generating content. Try again!</div>"
249
- yield (current_user_input, current_index, feed_items, feed_html, "", False, generate_progress_html(100, "Oops, something went wrong! πŸ˜…", user_input))
 
250
 
251
  def load_next(user_input, generate_video, current_index, feed_items):
252
  """Load the next item in the feed."""
253
  current_user_input = user_input.strip() or "trending"
 
254
  is_loading = True
255
- progress_html = generate_progress_html(0, f"Loading next {current_user_input} vibe... πŸš€", current_user_input)
256
  yield (current_user_input, current_index, feed_items, gr.update(), gr.update(), is_loading, progress_html)
257
 
258
  try:
259
  if current_index + 1 < len(feed_items):
260
  current_index += 1
261
- progress_html = generate_progress_html(50, f"Switching to the next {current_user_input} moment... πŸ”„", current_user_input)
262
  yield (gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), is_loading, progress_html)
263
  else:
264
  ideas = feed_items[-1]['ideas'] if feed_items else None
265
  if not ideas:
266
- ideas_gen = generate_ideas(current_user_input)
267
  for update in ideas_gen:
268
  if isinstance(update, tuple):
269
  progress, message = update
270
- progress_html = generate_progress_html(progress, message, current_user_input)
271
  yield (gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), is_loading, progress_html)
272
  else:
273
  ideas = update
274
-
275
- item_gen = generate_item(current_user_input, ideas, generate_video)
 
 
 
276
  for update in item_gen:
277
  if isinstance(update, tuple):
278
  progress, message = update
279
- progress_html = generate_progress_html(progress, message, current_user_input)
280
  yield (gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), is_loading, progress_html)
281
  else:
282
- feed_items.append(update)
 
283
  current_index = len(feed_items) - 1
284
-
285
- feed_html = generate_html(feed_items, False, current_index, current_user_input, is_loading=False)
 
 
 
286
  share_html = generate_share_links(feed_items[current_index]['image_base64'], feed_items[current_index]['video_base64'], feed_items[current_index]['text'])
287
  yield (current_user_input, current_index, feed_items, feed_html, share_html, False, "")
288
  except Exception as e:
289
  print(f"Error in load_next: {e}")
290
  feed_html = "<div style='color: white; text-align: center;'>Error generating content. Try again!</div>"
291
- yield (current_user_input, current_index, feed_items, feed_html, "", False, generate_progress_html(100, "Oops, something went wrong! πŸ˜…", current_user_input))
 
292
 
293
  def load_previous(user_input, generate_video, current_index, feed_items):
294
  """Load the previous item in the feed."""
@@ -299,37 +307,65 @@ def load_previous(user_input, generate_video, current_index, feed_items):
299
  share_html = generate_share_links(feed_items[current_index]['image_base64'], feed_items[current_index]['video_base64'], feed_items[current_index]['text'])
300
  return current_user_input, current_index, feed_items, feed_html, share_html, False, ""
301
 
302
- # Gradio Interface
303
- with gr.Blocks(css="""
304
- body { background-color: #000; color: #fff; font-family: Arial, sans-serif; }
305
- .gradio-container { max-width: 400px; margin: 0 auto; padding: 10px; }
306
- input, button { border-radius: 5px; background-color: #222; color: #fff; border: 1px solid #444; }
307
- button { background-color: #ff2d55; }
308
- button:hover { background-color: #e0264b; }
309
- .gr-button { width: 100%; margin-top: 10px; }
310
- .gr-form { background-color: #111; padding: 15px; border-radius: 10px; }
311
- """, title="Create Your Feed") as demo:
 
 
 
 
312
  current_user_input = gr.State(value="")
313
  current_index = gr.State(value=0)
314
  feed_items = gr.State(value=[])
315
  is_loading = gr.State(value=False)
 
316
 
 
317
  with gr.Column(elem_classes="gr-form"):
318
  gr.Markdown("### Create Your Feed")
319
- user_input = gr.Textbox(label="Enter Concept or Ideas", placeholder="e.g., jogging in gardens by the bay")
320
- generate_video_checkbox = gr.Checkbox(label="Generate Video (may take longer)", value=False)
 
 
 
 
 
 
 
 
321
  magic_button = gr.Button("✨ Generate Next Item", elem_classes="gr-button")
322
 
323
- progress_html = gr.HTML(label="Progress")
 
324
  feed_html = gr.HTML()
325
  share_html = gr.HTML(label="Share this item:")
326
 
327
- user_input.submit(start_feed, [user_input, generate_video_checkbox, current_index, feed_items],
328
- [current_user_input, current_index, feed_items, feed_html, share_html, is_loading, progress_html])
329
- magic_button.click(load_next, [user_input, generate_video_checkbox, current_index, feed_items],
330
- [current_user_input, current_index, feed_items, feed_html, share_html, is_loading, progress_html])
331
- previous_button = gr.Button("Previous", visible=False).click(load_previous,
332
- [user_input, generate_video_checkbox, current_index, feed_items],
333
- [current_user_input, current_index, feed_items, feed_html, share_html, is_loading, progress_html])
 
 
 
 
 
 
 
 
 
 
 
 
334
 
335
- demo.launch()
 
 
1
  import gradio as gr
 
 
2
  from PIL import Image
3
  from io import BytesIO
4
  import base64
 
 
5
  import random
6
  import urllib.parse
7
  import time
 
12
  if current_version < required_version:
13
  raise ValueError(f"Gradio version {current_version} is outdated. Please upgrade to {required_version} or later using 'pip install gradio=={required_version}'.")
14
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  def clean_response_text(response_text):
16
  """Clean API response by removing Markdown code block markers."""
17
  cleaned_text = response_text.strip()
 
24
  def generate_ideas(user_input):
25
  """Generate 5 creative TikTok video ideas based on user input."""
26
  yield (10, f"Brainstorming epic ideas for {user_input}... 🌟")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  try:
28
+ # Placeholder for actual API call (e.g., Google Generative AI)
29
+ ideas = [
30
+ f"A vibrant {user_input} scene with dynamic lighting",
31
+ f"A close-up of {user_input} in a futuristic city",
32
+ f"A high-energy {user_input} moment with bold colors",
33
+ f"A serene {user_input} scene with soft focus",
34
+ f"An action-packed {user_input} challenge"
35
+ ]
 
 
36
  yield (20, f"Ideas locked in for {user_input}! πŸš€")
37
+ yield ideas # Yield the final list
38
  except Exception as e:
39
  print(f"Error generating ideas: {e}")
40
  yield (20, f"Oops, tweaking the plan for {user_input}... πŸ”§")
41
+ default_ideas = [
42
  f"A dramatic {user_input} scene with cinematic lighting",
43
  f"A close-up of {user_input} in a futuristic setting",
44
+ f"A high-energy {user_input} moment with vibrant colors"
 
 
45
  ]
46
+ yield default_ideas # Yield default ideas on failure
47
 
48
  def generate_item(user_input, ideas, generate_video=False):
49
  """Generate a feed item (image and optionally video) with progress updates."""
50
+ yield (20, f"Crafting your {user_input} masterpiece... 🎨")
51
+ try:
52
+ selected_idea = random.choice(ideas) # Requires ideas to be a list
53
+ # Simulate image generation
54
+ image = Image.new('RGB', (360, 640), color='gray') # Placeholder
55
+ buffered = BytesIO()
56
+ image.save(buffered, format="PNG")
57
+ img_str = base64.b64encode(buffered.getvalue()).decode()
58
+ video_base64 = None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
  if generate_video:
60
+ yield (60, f"Filming a viral video for {user_input}... πŸŽ₯")
61
+ time.sleep(2) # Simulate video generation
62
+ # Placeholder for video generation
63
+ video_base64 = base64.b64encode(b"fake_video_data").decode()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  yield (95, f"Polishing your {user_input} masterpiece... ✨")
65
+ yield {
66
+ 'text': f"{selected_idea}! πŸ”₯ #{user_input.replace(' ', '')}",
67
+ 'image_base64': img_str,
68
+ 'video_base64': video_base64,
69
+ 'ideas': ideas
70
+ }
71
+ except Exception as e:
72
+ print(f"Error generating item: {e}")
73
+ yield (95, f"Falling back to placeholder for {user_input}... πŸ–ΌοΈ")
74
+ image = Image.new('RGB', (360, 640), color='gray')
75
+ buffered = BytesIO()
76
+ image.save(buffered, format="PNG")
77
+ img_str = base64.b64encode(buffered.getvalue()).decode()
78
+ yield {
79
+ 'text': f"Amazing {user_input}! πŸ”₯ #{user_input.replace(' ', '')}",
80
+ 'image_base64': img_str,
81
+ 'video_base64': None,
82
+ 'ideas': ideas
83
+ }
84
 
85
  def generate_progress_html(progress, message, user_input):
86
+ """Generate HTML for the progress bar and witty text."""
87
  return f"""
88
  <div id="progress-container" style="
89
  display: flex; flex-direction: column; align-items: center; justify-content: center;
 
103
  </div>
104
  """
105
 
106
+ def generate_html(feed_items, scroll_to_latest=False, current_index=0, user_input="", is_loading=False):
107
  """Generate HTML for the feed display."""
108
+ if is_loading:
109
+ return """
110
+ <div id="feed-container" style="
111
+ display: flex; flex-direction: column; align-items: center;
112
+ max-width: 360px; margin: 0 auto; background-color: #000; height: 640px;
113
+ border: 1px solid #333; border-radius: 10px; color: white;">
114
+ </div>
115
+ """
116
  if not feed_items or current_index >= len(feed_items):
117
+ return """
118
+ <div style="color: white; text-align: center; max-width: 360px; margin: 0 auto;">
119
+ Enter a concept or idea to start your feed!
120
+ </div>
121
+ """
122
  item = feed_items[current_index]
123
+ media_element = f"""
124
+ <img id="feed-image" src="data:image/png;base64,{item['image_base64']}" style="
125
+ width: 100%; height: 100%; object-fit: cover; position: absolute; top: 0; left: 0; z-index: 1;">
126
+ """
127
  if item['video_base64']:
128
+ media_element = f"""
129
+ <video id="feed-video" controls style="
130
+ width: 100%; height: 100%; object-fit: cover; position: absolute; top: 0; left: 0; z-index: 1;">
131
+ <source src="data:video/mp4;base64,{item['video_base64']}" type="video/mp4">
132
+ Your browser does not support the video tag.
133
+ </video>
134
+ """
135
  return f"""
136
+ <div id="feed-container" style="
137
+ display: flex; flex-direction: column; align-items: center;
138
+ max-width: 360px; margin: 0 auto; background-color: #000; height: 640px;
139
+ border: 1px solid #333; border-radius: 10px; position: relative;">
140
+ <div class="feed-item" style="
141
+ width: 100%; height: 100%; position: relative; display: flex;
142
+ flex-direction: column; justify-content: flex-end; overflow: hidden; cursor: pointer;"
143
+ onclick="handleClick(event)">
144
+ {media_element}
145
+ <div style="
146
+ position: relative; z-index: 2; background: linear-gradient(to top, rgba(0,0,0,0.7), transparent);
147
+ padding: 20px; color: white; font-family: Arial, sans-serif; font-size: 18px; font-weight: bold;">
148
+ {item['text']}
149
+ </div>
150
+ </div>
151
  </div>
152
+ <script>
153
+ function handleClick(event) {{
154
+ const media = document.getElementById('feed-video') || document.getElementById('feed-image');
155
+ const rect = media.getBoundingClientRect();
156
+ const clickX = event.clientX - rect.left;
157
+ const width = rect.width;
158
+ if (clickX > width * 0.75) {{
159
+ document.getElementById('previous-button').click();
160
+ }}
161
+ }}
162
+ </script>
163
  """
164
 
165
+ def generate_share_links(image_base64, video_base64, caption):
166
+ """Generate share links for social media platforms."""
167
+ image_data_url = f"data:image/png;base64,{image_base64}"
168
+ encoded_caption = urllib.parse.quote(caption)
169
+ download_links = f"""
170
+ <p style="text-align: center; margin-bottom: 10px;">Download the media to share:</p>
171
+ <div style="display: flex; flex-wrap: wrap; justify-content: center; gap: 8px; margin-bottom: 15px;">
172
+ <a href="{image_data_url}" download="feed_item.png" style="
173
+ background-color: #4CAF50; color: white; padding: 8px 16px; border-radius: 5px;
174
+ text-decoration: none; font-size: 14px; font-weight: bold;">Download Image</a>
175
+ """
176
+ if video_base64:
177
+ video_data_url = f"data:video/mp4;base64,{video_base64}"
178
+ download_links += f"""
179
+ <a href="{video_data_url}" download="feed_video.mp4" style="
180
+ background-color: #4CAF50; color: white; padding: 8px 16px; border-radius: 5px;
181
+ text-decoration: none; font-size: 14px; font-weight: bold;">Download Video</a>
182
+ """
183
+ download_links += "</div>"
184
+ instruction = """
185
+ <p style="text-align: center; margin-bottom: 10px;">
186
+ Click a share button below to start a post with the caption, then manually upload the downloaded media.
187
+ </p>
188
+ """
189
+ share_links = f"""
190
+ <div style="display: flex; flex-wrap: wrap; justify-content: center; gap: 8px; margin-bottom: 15px;">
191
+ <a href="https://studio.youtube.com/channel/UC/videos/upload?description={encoded_caption}" target="_blank" style="
192
+ background-color: #ff0000; color: white; padding: 8px 16px; border-radius: 5px;
193
+ text-decoration: none; font-size: 14px; font-weight: bold;">Share to YouTube as a Short</a>
194
+ </div>
195
+ """
196
+ return f"""
197
+ <div style="display: flex; flex-direction: column; align-items: center; gap: 10px; margin-top: 10px; color: white;">
198
+ {download_links}
199
+ {instruction}
200
+ {share_links}
201
+ </div>
202
+ """
203
 
204
  def start_feed(user_input, generate_video, current_index, feed_items):
205
+ """Start or update the feed based on user input."""
206
  user_input = user_input.strip() or "trending"
207
  current_user_input = user_input
208
  is_loading = True
 
218
  progress_html = generate_progress_html(progress, message, user_input)
219
  yield (gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), is_loading, progress_html)
220
  else:
221
+ ideas = update # Final list of ideas
222
+
223
+ if ideas is None or not isinstance(ideas, list):
224
+ raise ValueError("Failed to generate ideas")
225
+
226
  item_gen = generate_item(user_input, ideas, generate_video)
227
  item = None
228
  for update in item_gen:
 
231
  progress_html = generate_progress_html(progress, message, user_input)
232
  yield (gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), is_loading, progress_html)
233
  else:
234
+ item = update # Final item dictionary
235
+
236
+ if item is None or not isinstance(item, dict):
237
+ raise ValueError("Failed to generate item")
238
+
239
  feed_items = [item]
240
  current_index = 0
241
  feed_html = generate_html(feed_items, False, current_index, user_input, is_loading=False)
 
244
  except Exception as e:
245
  print(f"Error in start_feed: {e}")
246
  feed_html = "<div style='color: white; text-align: center;'>Error generating content. Try again!</div>"
247
+ progress_html = generate_progress_html(100, "Oops, something went wrong! πŸ˜…", user_input)
248
+ yield (current_user_input, current_index, feed_items, feed_html, "", False, progress_html)
249
 
250
  def load_next(user_input, generate_video, current_index, feed_items):
251
  """Load the next item in the feed."""
252
  current_user_input = user_input.strip() or "trending"
253
+ user_input = current_user_input
254
  is_loading = True
255
+ progress_html = generate_progress_html(0, f"Loading next {user_input} vibe... πŸš€", user_input)
256
  yield (current_user_input, current_index, feed_items, gr.update(), gr.update(), is_loading, progress_html)
257
 
258
  try:
259
  if current_index + 1 < len(feed_items):
260
  current_index += 1
261
+ progress_html = generate_progress_html(50, f"Switching to the next {user_input} moment... πŸ”„", user_input)
262
  yield (gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), is_loading, progress_html)
263
  else:
264
  ideas = feed_items[-1]['ideas'] if feed_items else None
265
  if not ideas:
266
+ ideas_gen = generate_ideas(user_input)
267
  for update in ideas_gen:
268
  if isinstance(update, tuple):
269
  progress, message = update
270
+ progress_html = generate_progress_html(progress, message, user_input)
271
  yield (gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), is_loading, progress_html)
272
  else:
273
  ideas = update
274
+
275
+ if ideas is None or not isinstance(ideas, list):
276
+ raise ValueError("Failed to generate ideas")
277
+
278
+ item_gen = generate_item(user_input, ideas, generate_video)
279
  for update in item_gen:
280
  if isinstance(update, tuple):
281
  progress, message = update
282
+ progress_html = generate_progress_html(progress, message, user_input)
283
  yield (gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), is_loading, progress_html)
284
  else:
285
+ item = update
286
+ feed_items.append(item)
287
  current_index = len(feed_items) - 1
288
+
289
+ if item is None or not isinstance(item, dict):
290
+ raise ValueError("Failed to generate item")
291
+
292
+ feed_html = generate_html(feed_items, False, current_index, user_input, is_loading=False)
293
  share_html = generate_share_links(feed_items[current_index]['image_base64'], feed_items[current_index]['video_base64'], feed_items[current_index]['text'])
294
  yield (current_user_input, current_index, feed_items, feed_html, share_html, False, "")
295
  except Exception as e:
296
  print(f"Error in load_next: {e}")
297
  feed_html = "<div style='color: white; text-align: center;'>Error generating content. Try again!</div>"
298
+ progress_html = generate_progress_html(100, "Oops, something went wrong! πŸ˜…", user_input)
299
+ yield (current_user_input, current_index, feed_items, feed_html, "", False, progress_html)
300
 
301
  def load_previous(user_input, generate_video, current_index, feed_items):
302
  """Load the previous item in the feed."""
 
307
  share_html = generate_share_links(feed_items[current_index]['image_base64'], feed_items[current_index]['video_base64'], feed_items[current_index]['text'])
308
  return current_user_input, current_index, feed_items, feed_html, share_html, False, ""
309
 
310
+ # Define the Gradio interface
311
+ with gr.Blocks(
312
+ css="""
313
+ body { background-color: #000; color: #fff; font-family: Arial, sans-serif; }
314
+ .gradio-container { max-width: 400px; margin: 0 auto; padding: 10px; }
315
+ input, select, button, .gr-checkbox { border-radius: 5px; background-color: #222; color: #fff; border: 1px solid #444; }
316
+ button { background-color: #ff2d55; border: none; }
317
+ button:hover { background-color: #e0264b; }
318
+ .gr-button { width: 100%; margin-top: 10px; }
319
+ .gr-form { background-color: #111; padding: 15px; border-radius: 10px; }
320
+ """,
321
+ title="Create Your Feed"
322
+ ) as demo:
323
+ # State variables
324
  current_user_input = gr.State(value="")
325
  current_index = gr.State(value=0)
326
  feed_items = gr.State(value=[])
327
  is_loading = gr.State(value=False)
328
+ share_links = gr.State(value="")
329
 
330
+ # Input section
331
  with gr.Column(elem_classes="gr-form"):
332
  gr.Markdown("### Create Your Feed")
333
+ user_input = gr.Textbox(
334
+ label="Enter Concept or Ideas",
335
+ value="",
336
+ placeholder="e.g., sushi adventure, neon tech",
337
+ submit_btn=False
338
+ )
339
+ generate_video_checkbox = gr.Checkbox(
340
+ label="Generate Video (may take longer)",
341
+ value=False
342
+ )
343
  magic_button = gr.Button("✨ Generate Next Item", elem_classes="gr-button")
344
 
345
+ # Output display
346
+ progress_html = gr.HTML(label="Progress", visible=True)
347
  feed_html = gr.HTML()
348
  share_html = gr.HTML(label="Share this item:")
349
 
350
+ # Event handlers
351
+ user_input.submit(
352
+ fn=start_feed,
353
+ inputs=[user_input, generate_video_checkbox, current_index, feed_items],
354
+ outputs=[current_user_input, current_index, feed_items, feed_html, share_html, is_loading, progress_html]
355
+ )
356
+
357
+ magic_button.click(
358
+ fn=load_next,
359
+ inputs=[user_input, generate_video_checkbox, current_index, feed_items],
360
+ outputs=[current_user_input, current_index, feed_items, feed_html, share_html, is_loading, progress_html]
361
+ )
362
+
363
+ previous_button = gr.Button("Previous", elem_id="previous-button", visible=False)
364
+ previous_button.click(
365
+ fn=load_previous,
366
+ inputs=[user_input, generate_video_checkbox, current_index, feed_items],
367
+ outputs=[current_user_input, current_index, feed_items, feed_html, share_html, is_loading, progress_html]
368
+ )
369
 
370
+ # Launch the app with share=True for public link
371
+ demo.launch(share=True) # Note: share=True requires a Gradio account and may have usage limits