ladybug11 commited on
Commit
d555e1d
Β·
1 Parent(s): 14d2c66

add caption hastag

Browse files
Files changed (1) hide show
  1. app.py +93 -44
app.py CHANGED
@@ -33,7 +33,7 @@ hybrid_quote_generator = HybridQuoteGenerator(
33
  openai_client=openai_client,
34
  )
35
 
36
- # Initialize MCP Client (optional)
37
  try:
38
  mcp_client = MCPClient("https://abidlabs-mcp-tools.hf.space")
39
  mcp_enabled = True
@@ -220,6 +220,62 @@ def get_trend_insights(niche: str) -> Dict[str, Any]:
220
  return trends.get(niche, default)
221
 
222
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
223
  # =============================================================================
224
  # TOOLS
225
  # =============================================================================
@@ -269,20 +325,6 @@ def search_pexels_video_tool(style: str, niche: str, trend_label: str = "") -> d
269
  """
270
  Search and fetch a matching vertical video from Pexels based on style, niche,
271
  and the current trend label.
272
-
273
- Args:
274
- style: Visual style for the background (e.g. Cinematic, Nature, Urban).
275
- niche: Content niche (e.g. Motivation, Business/Entrepreneurship, Fitness).
276
- trend_label: Short description of the trend theme used to bias search
277
- queries (e.g. "soft life vs discipline era").
278
-
279
- Returns:
280
- A dictionary with:
281
- - success: Whether any suitable video was found.
282
- - video_url: Direct URL to an MP4 file (if success is True).
283
- - search_query: The text query used for the Pexels search.
284
- - pexels_url: Link to the Pexels video page.
285
- - error: Error message if success is False.
286
  """
287
  base_queries = {
288
  "Motivation": {
@@ -405,19 +447,6 @@ def create_quote_video_tool(
405
  ) -> dict:
406
  """
407
  Create the final quote video via the Modal web endpoint.
408
-
409
- Args:
410
- video_url: Direct URL to the background video file (MP4) from Pexels.
411
- quote_text: The text of the quote to render on top of the video.
412
- output_path: Local filesystem path where the rendered video will be saved.
413
- audio_b64: Optional base64-encoded audio bytes for voice-over narration.
414
- text_style: Layout style for the quote text (e.g. 'classic_center').
415
-
416
- Returns:
417
- A dictionary with:
418
- - success: Whether video rendering succeeded.
419
- - output_path: Path to the saved MP4 file if successful, else None.
420
- - message: Human-readable status or error message.
421
  """
422
  if not MODAL_ENDPOINT_URL:
423
  return {
@@ -621,7 +650,11 @@ def mcp_agent_pipeline(
621
  text_style: str,
622
  voice_profile: str,
623
  num_variations: int = 1,
624
- ) -> Tuple[str, List[str]]:
 
 
 
 
625
  status_log: List[str] = []
626
  status_log.append("πŸ€– **MCP-STYLE AGENT PIPELINE START**\n")
627
 
@@ -656,7 +689,7 @@ def mcp_agent_pipeline(
656
  quote = generate_quote_tool(niche, style, persona)
657
  if quote.startswith("Error"):
658
  status_log.append(f" ❌ Quote generation error: {quote}")
659
- return "\n".join(status_log), []
660
 
661
  preview = quote if len(quote) <= 140 else quote[:140] + "..."
662
  status_log.append(f" βœ… Quote: β€œ{preview}”\n")
@@ -695,7 +728,7 @@ def mcp_agent_pipeline(
695
 
696
  if not video_results:
697
  status_log.append("\n❌ No background videos found. Aborting.")
698
- return "\n".join(status_log), []
699
 
700
  status_log.append("")
701
 
@@ -741,7 +774,7 @@ def mcp_agent_pipeline(
741
 
742
  if not created_videos:
743
  status_log.append("\n❌ All video renderings failed.")
744
- return "\n".join(status_log), []
745
 
746
  status_log.append("\nπŸ”— **Integrations used:**")
747
  status_log.append(" β€’ Gemini – quote + variety tracking")
@@ -752,10 +785,16 @@ def mcp_agent_pipeline(
752
  if mcp_enabled:
753
  status_log.append(" β€’ MCP server – available for extended tools")
754
 
 
 
 
 
 
 
755
  status_log.append("\n✨ **Pipeline complete!**")
756
  status_log.append(f" Generated {len(created_videos)} video variation(s).")
757
 
758
- return "\n".join(status_log), created_videos
759
 
760
 
761
  # =============================================================================
@@ -794,7 +833,7 @@ with gr.Blocks(
794
  ### MCP-style agent β€’ Gemini + OpenAI + ElevenLabs + Modal
795
 
796
  An autonomous mini-studio that generates trend-aware quote videos with voice-over,
797
- cinematic stock footage, and MCP-style agent reasoning.
798
  """
799
  )
800
 
@@ -880,11 +919,21 @@ with gr.Blocks(
880
  show_label=False,
881
  )
882
 
883
- gr.Markdown("### ✨ Your Quote Videos (This Run)")
 
884
  with gr.Row():
885
- video1 = gr.Video(label="Video 1", height=480)
886
- video2 = gr.Video(label="Video 2", height=480)
887
- video3 = gr.Video(label="Video 3", height=480)
 
 
 
 
 
 
 
 
 
888
 
889
  gr.Markdown(
890
  """
@@ -892,8 +941,8 @@ with gr.Blocks(
892
  ### 🧩 Under the hood
893
  - Context engineering: niche + persona + trend theme
894
  - Mini-RAG: curated trend knowledge feeding into generation
895
- - Hybrid LLM: Gemini (quotes) + OpenAI (commentary)
896
- - Multimodal pipeline: text β†’ audio β†’ video
897
  """
898
  )
899
 
@@ -905,7 +954,7 @@ with gr.Blocks(
905
  voice_profile_val,
906
  num_variations_val,
907
  ):
908
- status, videos = mcp_agent_pipeline(
909
  niche=niche_val,
910
  style=style_val,
911
  persona=persona_val,
@@ -926,7 +975,7 @@ with gr.Blocks(
926
  g5 = gallery_vids[4] if len(gallery_vids) > 4 else None
927
  g6 = gallery_vids[5] if len(gallery_vids) > 5 else None
928
 
929
- return status, v1, v2, v3, g1, g2, g3, g4, g5, g6
930
 
931
  generate_btn.click(
932
  process_and_display,
@@ -943,6 +992,7 @@ with gr.Blocks(
943
  video1,
944
  video2,
945
  video3,
 
946
  gallery_video1,
947
  gallery_video2,
948
  gallery_video3,
@@ -971,5 +1021,4 @@ with gr.Blocks(
971
  )
972
 
973
  if __name__ == "__main__":
974
- demo.launch(allowed_paths=["/data/gallery_videos"])
975
-
 
33
  openai_client=openai_client,
34
  )
35
 
36
+ # Initialize MCP Client (optional, not critical if missing)
37
  try:
38
  mcp_client = MCPClient("https://abidlabs-mcp-tools.hf.space")
39
  mcp_enabled = True
 
220
  return trends.get(niche, default)
221
 
222
 
223
+ # =============================================================================
224
+ # CAPTION + HASHTAG GENERATION (non-MCP version)
225
+ # =============================================================================
226
+
227
+ def generate_caption_and_hashtags(niche: str, persona: str, trend_label: str) -> str:
228
+ """
229
+ Generate a posting-ready caption, hashtags, and a tiny posting tip
230
+ based on niche + persona + trend theme.
231
+ """
232
+ persona_instruction = get_persona_instruction(persona)
233
+
234
+ prompt = f"""
235
+ Generate a social-media-ready caption and hashtags for a short vertical quote video.
236
+
237
+ Niche: {niche}
238
+ Persona / tone: {persona} ({persona_instruction})
239
+ Trend theme: {trend_label}
240
+
241
+ Requirements:
242
+ - CAPTION: 1–2 sentences max
243
+ * Should sound natural, like a human writing for TikTok/Instagram
244
+ * Should NOT repeat the quote text word-for-word
245
+ * Can reference feelings, situation, or transformation implied by the quote
246
+ - HASHTAGS:
247
+ * 8–12 hashtags total
248
+ * Mix of trending-style tags and niche / long-tail tags
249
+ * Use lowercase, no spaces (standard hashtag conventions)
250
+ * No banned, misleading, or spammy tags
251
+ - POSTING TIP:
252
+ * 1 short sentence with a practical suggestion (sound choice, posting time, or CTA)
253
+
254
+ Format the answer EXACTLY like this:
255
+
256
+ CAPTION:
257
+ <caption text>
258
+
259
+ HASHTAGS:
260
+ #tag1 #tag2 #tag3 ...
261
+
262
+ POSTING TIP:
263
+ <one short tip>
264
+ """
265
+
266
+ try:
267
+ completion = openai_client.chat.completions.create(
268
+ model="gpt-4o-mini",
269
+ messages=[{"role": "user", "content": prompt}],
270
+ max_tokens=220,
271
+ temperature=0.8,
272
+ )
273
+ text = completion.choices[0].message.content.strip()
274
+ return text
275
+ except Exception as e:
276
+ return f"Error generating caption/hashtags: {str(e)}"
277
+
278
+
279
  # =============================================================================
280
  # TOOLS
281
  # =============================================================================
 
325
  """
326
  Search and fetch a matching vertical video from Pexels based on style, niche,
327
  and the current trend label.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
328
  """
329
  base_queries = {
330
  "Motivation": {
 
447
  ) -> dict:
448
  """
449
  Create the final quote video via the Modal web endpoint.
 
 
 
 
 
 
 
 
 
 
 
 
 
450
  """
451
  if not MODAL_ENDPOINT_URL:
452
  return {
 
650
  text_style: str,
651
  voice_profile: str,
652
  num_variations: int = 1,
653
+ ) -> Tuple[str, List[str], str]:
654
+ """
655
+ Returns:
656
+ status_log_str, created_videos, caption_block
657
+ """
658
  status_log: List[str] = []
659
  status_log.append("πŸ€– **MCP-STYLE AGENT PIPELINE START**\n")
660
 
 
689
  quote = generate_quote_tool(niche, style, persona)
690
  if quote.startswith("Error"):
691
  status_log.append(f" ❌ Quote generation error: {quote}")
692
+ return "\n".join(status_log), [], ""
693
 
694
  preview = quote if len(quote) <= 140 else quote[:140] + "..."
695
  status_log.append(f" βœ… Quote: β€œ{preview}”\n")
 
728
 
729
  if not video_results:
730
  status_log.append("\n❌ No background videos found. Aborting.")
731
+ return "\n".join(status_log), [], ""
732
 
733
  status_log.append("")
734
 
 
774
 
775
  if not created_videos:
776
  status_log.append("\n❌ All video renderings failed.")
777
+ return "\n".join(status_log), [], ""
778
 
779
  status_log.append("\nπŸ”— **Integrations used:**")
780
  status_log.append(" β€’ Gemini – quote + variety tracking")
 
785
  if mcp_enabled:
786
  status_log.append(" β€’ MCP server – available for extended tools")
787
 
788
+ # Step 6 – Caption + hashtags block (returned separately for the UI)
789
+ status_log.append(
790
+ "\nπŸ“ **Step 6 – Caption + Hashtags** (see the panel next to your videos to copy-paste)"
791
+ )
792
+ caption_block = generate_caption_and_hashtags(niche, persona, trend_label)
793
+
794
  status_log.append("\n✨ **Pipeline complete!**")
795
  status_log.append(f" Generated {len(created_videos)} video variation(s).")
796
 
797
+ return "\n".join(status_log), created_videos, caption_block
798
 
799
 
800
  # =============================================================================
 
833
  ### MCP-style agent β€’ Gemini + OpenAI + ElevenLabs + Modal
834
 
835
  An autonomous mini-studio that generates trend-aware quote videos with voice-over,
836
+ cinematic stock footage, and ready-to-post captions + hashtags.
837
  """
838
  )
839
 
 
919
  show_label=False,
920
  )
921
 
922
+ gr.Markdown("### ✨ Your Quote Videos & Caption")
923
+
924
  with gr.Row():
925
+ with gr.Column(scale=3):
926
+ with gr.Row():
927
+ video1 = gr.Video(label="Video 1", height=420)
928
+ video2 = gr.Video(label="Video 2", height=420)
929
+ video3 = gr.Video(label="Video 3", height=420)
930
+ with gr.Column(scale=2):
931
+ caption_box = gr.Textbox(
932
+ label="πŸ“„ Caption + Hashtags + Posting Tip",
933
+ lines=14,
934
+ show_label=True,
935
+ interactive=False,
936
+ )
937
 
938
  gr.Markdown(
939
  """
 
941
  ### 🧩 Under the hood
942
  - Context engineering: niche + persona + trend theme
943
  - Mini-RAG: curated trend knowledge feeding into generation
944
+ - Hybrid LLM: Gemini (quotes) + OpenAI (commentary & captions)
945
+ - Multimodal pipeline: text β†’ audio β†’ video β†’ posting assets
946
  """
947
  )
948
 
 
954
  voice_profile_val,
955
  num_variations_val,
956
  ):
957
+ status, videos, caption_block = mcp_agent_pipeline(
958
  niche=niche_val,
959
  style=style_val,
960
  persona=persona_val,
 
975
  g5 = gallery_vids[4] if len(gallery_vids) > 4 else None
976
  g6 = gallery_vids[5] if len(gallery_vids) > 5 else None
977
 
978
+ return status, v1, v2, v3, caption_block, g1, g2, g3, g4, g5, g6
979
 
980
  generate_btn.click(
981
  process_and_display,
 
992
  video1,
993
  video2,
994
  video3,
995
+ caption_box,
996
  gallery_video1,
997
  gallery_video2,
998
  gallery_video3,
 
1021
  )
1022
 
1023
  if __name__ == "__main__":
1024
+ demo.launch(allowed_paths=["/data/gallery_videos"])