anupamdas commited on
Commit
80b2a07
Β·
verified Β·
1 Parent(s): e1459e5

Update streamlit_app.py

Browse files
Files changed (1) hide show
  1. streamlit_app.py +223 -223
streamlit_app.py CHANGED
@@ -1,224 +1,224 @@
1
- import streamlit as st
2
- import os
3
- import re
4
- import base64
5
- import tempfile
6
- import google.generativeai as genai
7
- import time
8
-
9
- # Local imports from your other project files
10
- from agents import get_content_enhancer_agent
11
- from tools.image_tool import ImageGenerationTool
12
- from local_knowledge_base import load_knowledge_base
13
-
14
- # --- Page and API Configuration ---
15
- st.set_page_config(page_title="Video Analyzer", layout="wide")
16
- st.title("πŸŽ₯ Video Analysis & Content Generation")
17
- st.markdown("Provide a video file or a YouTube link to generate an enriched summary with AI-generated images.")
18
-
19
- # --- Authentication and Initialization ---
20
- try:
21
- genai.configure(api_key=st.secrets["GOOGLE_API_KEY"])
22
- except KeyError as e:
23
- st.error(f"Secret key '{e.args[0]}' not found. Please add it to your .streamlit/secrets.toml file.")
24
- st.stop()
25
-
26
- @st.cache_resource(show_spinner="Loading Local Knowledge Base...")
27
- def get_local_kb():
28
- return load_knowledge_base()
29
-
30
-
31
- local_kb = get_local_kb()
32
- local_kb.load(recreate=False) # Load the knowledge base into memory
33
- # --- Core Logic Functions ---
34
-
35
- def enrich_and_generate_images(video_summary: str) -> str:
36
- """
37
- Takes a raw summary, enriches it using an AI agent, and generates images for placeholders.
38
- This is a helper function used by both local file and YouTube workflows.
39
- """
40
- # --- Step 2: Enhance Content with AI Agent ---
41
- st.write("### Step 2: Enhancing Content with AI Agent...")
42
- with st.spinner("The content agent is adding details and finding opportunities for images..."):
43
- content_agent = get_content_enhancer_agent(knowledge_base=local_kb)
44
- agent_response_generator = content_agent.run(video_summary)
45
- enriched_text_with_placeholders = "".join(list(agent_response_generator))
46
-
47
- st.success("βœ… Content enhancement complete.")
48
- with st.expander("See Enriched Text with Image Placeholders"):
49
- st.text(enriched_text_with_placeholders)
50
-
51
- # --- Step 3: Generate Images and Assemble Final Document ---
52
- st.write("### Step 3: Generating Images and Assembling Final Document...")
53
- final_output = enriched_text_with_placeholders
54
-
55
- try:
56
- # Get Clipdrop API key from secrets
57
- image_tool = ImageGenerationTool(api_key=st.secrets["CLIPDROP_API_KEY"])
58
- except KeyError:
59
- st.error("CLIPDROP_API_KEY not found in Streamlit Secrets. Please add it to continue.")
60
- st.stop()
61
-
62
- placeholders = re.findall(r'\[IMAGE: caption="(.*?)"\]', final_output)
63
- if not placeholders:
64
- st.warning("The agent did not add any image placeholders. Returning the enhanced text.")
65
- return final_output
66
-
67
- total_images = len(placeholders)
68
- progress_bar = st.progress(0, text=f"Preparing to generate {total_images} images...")
69
-
70
- for i, caption_prompt in enumerate(placeholders):
71
- progress_text = f"Generating image {i+1} of {total_images}: '{caption_prompt[:40]}...'"
72
- progress_bar.progress((i) / total_images, text=progress_text)
73
-
74
- image_path = image_tool.generate_image(prompt=caption_prompt)
75
-
76
- placeholder_to_replace = f'[IMAGE: caption="{caption_prompt}"]'
77
- if "Error:" in image_path or not os.path.exists(image_path):
78
- markdown_tag = f'\n> *Image generation failed for prompt: "{caption_prompt}". Reason: {image_path}*\n'
79
- else:
80
- with open(image_path, "rb") as f:
81
- image_bytes = f.read()
82
- base64_image = base64.b64encode(image_bytes).decode("utf-8")
83
- markdown_tag = f'\n<div align="center" style="margin: 1rem 0;"><img src="data:image/png;base64,{base64_image}" alt="{caption_prompt}" style="max-width: 100%; max-height: 500px; border-radius: 8px; box-shadow: 0 4px 8px rgba(0,0,0,0.1);"><br><em style="font-size: 0.9em; color: #555;">{caption_prompt}</em></div>\n'
84
-
85
- final_output = final_output.replace(placeholder_to_replace, markdown_tag, 1)
86
-
87
- if i < total_images - 1:
88
- time.sleep(5) # Respect API rate limits
89
-
90
- progress_bar.progress(1.0, text="All images generated!")
91
- st.success("βœ… All images generated.")
92
- return final_output
93
-
94
- def run_youtube_analysis_workflow(yt_url: str):
95
- """
96
- Analyzes a YouTube video directly from its URL without downloading.
97
- """
98
- st.write("### Step 1: Analyzing YouTube Video...")
99
- st.info("Sending URL to Gemini for analysis...")
100
-
101
- try:
102
- # The key is to use a tool that tells the model how to handle the YouTube URL.
103
- from google.ai.generativelanguage import Part, FileData
104
-
105
- video_analysis_model = genai.GenerativeModel('models/gemini-1.5-flash-latest')
106
-
107
- prompt = "Analyze this video then compile the top 2-3 core topics and then write a detailed summary on it. Maintain the original order of events."
108
-
109
- # Create the FileData part using the YouTube URI
110
- video_part = Part(
111
- file_data=FileData(
112
- mime_type="video/youtube",
113
- file_uri=yt_url
114
- )
115
- )
116
-
117
- with st.spinner("Gemini is processing the video... This may take a few minutes for longer videos."):
118
- response = video_analysis_model.generate_content([prompt, video_part], request_options={"timeout": 600})
119
-
120
- video_summary = response.text
121
- st.success("βœ… YouTube video analysis complete.")
122
- with st.expander("See Raw Video Summary"):
123
- st.markdown(video_summary)
124
-
125
- # Now, pass the summary to the shared enrichment/image generation stage
126
- return enrich_and_generate_images(video_summary)
127
-
128
- except Exception as e:
129
- st.error(f"An error occurred during YouTube analysis: {e}")
130
- st.info("This can sometimes happen with private, age-restricted, or very new videos. Please check the URL and try again.")
131
- return None
132
-
133
- def run_local_file_analysis_workflow(video_path: str):
134
- """
135
- Analyzes a video from a local file path by uploading it to Google.
136
- """
137
- video_file_obj = None
138
- try:
139
- st.write("### Step 1: Uploading & Analyzing Local Video File...")
140
- st.info("Uploading video file to Google. This may take a moment...")
141
- video_file_obj = genai.upload_file(path=video_path)
142
- st.info(f"Video uploaded: {video_file_obj.name}. Waiting for processing...")
143
-
144
- while video_file_obj.state.name == "PROCESSING":
145
- time.sleep(5)
146
- video_file_obj = genai.get_file(video_file_obj.name)
147
-
148
- if video_file_obj.state.name == "FAILED":
149
- st.error(f"Video processing failed: {video_file_obj.state}")
150
- return None
151
-
152
- st.success("βœ… Video is processed and ready for analysis.")
153
-
154
- video_analysis_model = genai.GenerativeModel('models/gemini-1.5-flash-latest')
155
- prompt = ("Analyze this video in detail. First, identify the 2-3 core topics discussed. "
156
- "Then, write a comprehensive summary of the video's content, maintaining the original order of events.")
157
-
158
- response = video_analysis_model.generate_content([prompt, video_file_obj], request_options={"timeout": 600})
159
- video_summary = response.text
160
- st.success("βœ… Local video analysis complete.")
161
- with st.expander("See Raw Video Summary"):
162
- st.markdown(video_summary)
163
-
164
- # Pass the summary to the shared enrichment/image generation stage
165
- return enrich_and_generate_images(video_summary)
166
-
167
- except Exception as e:
168
- st.error(f"An error occurred during local file analysis: {e}")
169
- return None
170
- finally:
171
- if video_file_obj:
172
- genai.delete_file(video_file_obj.name)
173
- st.info(f"Cleaned up cloud file: {video_file_obj.name}")
174
-
175
-
176
- # --- Streamlit UI ---
177
-
178
- st.divider()
179
- st.subheader("Select Video Source")
180
-
181
- # Use tabs for a clean UI. We recommend YouTube for speed and reliability.
182
- tab1, tab2 = st.tabs(["πŸ”— **Use a YouTube Link (Recommended)**", "πŸ“€ Upload a Video File"])
183
- video_path = None
184
- final_response = None
185
-
186
- with tab1:
187
- yt_url = st.text_input(
188
- "Enter YouTube Video URL",
189
- placeholder="https://www.youtube.com/watch?v=..."
190
- )
191
-
192
- with tab2:
193
- video_file = st.file_uploader(
194
- "Choose a video file",
195
- type=["mp4", "mov", "avi", "mpeg", "webm"]
196
- )
197
-
198
- if st.button("πŸš€ Analyze and Generate Content", use_container_width=True):
199
- # Prioritize YouTube URL if both are provided
200
- if yt_url:
201
- final_response = run_youtube_analysis_workflow(yt_url)
202
- elif video_file:
203
- with st.spinner("Saving uploaded file locally..."):
204
- with tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") as tmp_file:
205
- tmp_file.write(video_file.getbuffer())
206
- video_path = tmp_file.name
207
-
208
- try:
209
- final_response = run_local_file_analysis_workflow(video_path)
210
- finally:
211
- if video_path and os.path.exists(video_path):
212
- os.remove(video_path)
213
- st.info(f"Cleaned up local temporary file: {video_path}")
214
- else:
215
- st.warning("Please provide a YouTube URL or upload a video file.")
216
- st.stop()
217
-
218
- # Display the final generated content if the workflow was successful
219
- if final_response:
220
- st.divider()
221
- st.markdown("## ✨ Final Generated Content")
222
- st.markdown(final_response, unsafe_allow_html=True)
223
- else:
224
  st.error("The analysis workflow did not produce a final output. Please check the errors above.")
 
1
+ import streamlit as st
2
+ import os
3
+ import re
4
+ import base64
5
+ import tempfile
6
+ import google.generativeai as genai
7
+ import time
8
+
9
+ # Local imports from your other project files
10
+ from agents import get_content_enhancer_agent
11
+ from tools.image_tool import ImageGenerationTool
12
+ from local_knowledge_base import load_knowledge_base
13
+
14
+ # --- Page and API Configuration ---
15
+ st.set_page_config(page_title="Video Analyzer", layout="wide")
16
+ st.title("πŸŽ₯ Video Analysis & Content Generation")
17
+ st.markdown("Provide a video file or a YouTube link to generate an enriched summary with AI-generated images.")
18
+
19
+ # --- Authentication and Initialization ---
20
+ try:
21
+ genai.configure(api_key=os.getenv("GOOGLE_API_KEY"))
22
+ except KeyError as e:
23
+ st.error(f"Secret key '{e.args[0]}' not found. Please add it to your .streamlit/secrets.toml file.")
24
+ st.stop()
25
+
26
+ @st.cache_resource(show_spinner="Loading Local Knowledge Base...")
27
+ def get_local_kb():
28
+ return load_knowledge_base()
29
+
30
+
31
+ local_kb = get_local_kb()
32
+ local_kb.load(recreate=False) # Load the knowledge base into memory
33
+ # --- Core Logic Functions ---
34
+
35
+ def enrich_and_generate_images(video_summary: str) -> str:
36
+ """
37
+ Takes a raw summary, enriches it using an AI agent, and generates images for placeholders.
38
+ This is a helper function used by both local file and YouTube workflows.
39
+ """
40
+ # --- Step 2: Enhance Content with AI Agent ---
41
+ st.write("### Step 2: Enhancing Content with AI Agent...")
42
+ with st.spinner("The content agent is adding details and finding opportunities for images..."):
43
+ content_agent = get_content_enhancer_agent(knowledge_base=local_kb)
44
+ agent_response_generator = content_agent.run(video_summary)
45
+ enriched_text_with_placeholders = "".join(list(agent_response_generator))
46
+
47
+ st.success("βœ… Content enhancement complete.")
48
+ with st.expander("See Enriched Text with Image Placeholders"):
49
+ st.text(enriched_text_with_placeholders)
50
+
51
+ # --- Step 3: Generate Images and Assemble Final Document ---
52
+ st.write("### Step 3: Generating Images and Assembling Final Document...")
53
+ final_output = enriched_text_with_placeholders
54
+
55
+ try:
56
+ # Get Clipdrop API key from secrets
57
+ image_tool = ImageGenerationTool(api_key=os.getenv("CLIPDROP_API_KEY"))
58
+ except KeyError:
59
+ st.error("CLIPDROP_API_KEY not found in Streamlit Secrets. Please add it to continue.")
60
+ st.stop()
61
+
62
+ placeholders = re.findall(r'\[IMAGE: caption="(.*?)"\]', final_output)
63
+ if not placeholders:
64
+ st.warning("The agent did not add any image placeholders. Returning the enhanced text.")
65
+ return final_output
66
+
67
+ total_images = len(placeholders)
68
+ progress_bar = st.progress(0, text=f"Preparing to generate {total_images} images...")
69
+
70
+ for i, caption_prompt in enumerate(placeholders):
71
+ progress_text = f"Generating image {i+1} of {total_images}: '{caption_prompt[:40]}...'"
72
+ progress_bar.progress((i) / total_images, text=progress_text)
73
+
74
+ image_path = image_tool.generate_image(prompt=caption_prompt)
75
+
76
+ placeholder_to_replace = f'[IMAGE: caption="{caption_prompt}"]'
77
+ if "Error:" in image_path or not os.path.exists(image_path):
78
+ markdown_tag = f'\n> *Image generation failed for prompt: "{caption_prompt}". Reason: {image_path}*\n'
79
+ else:
80
+ with open(image_path, "rb") as f:
81
+ image_bytes = f.read()
82
+ base64_image = base64.b64encode(image_bytes).decode("utf-8")
83
+ markdown_tag = f'\n<div align="center" style="margin: 1rem 0;"><img src="data:image/png;base64,{base64_image}" alt="{caption_prompt}" style="max-width: 100%; max-height: 500px; border-radius: 8px; box-shadow: 0 4px 8px rgba(0,0,0,0.1);"><br><em style="font-size: 0.9em; color: #555;">{caption_prompt}</em></div>\n'
84
+
85
+ final_output = final_output.replace(placeholder_to_replace, markdown_tag, 1)
86
+
87
+ if i < total_images - 1:
88
+ time.sleep(5) # Respect API rate limits
89
+
90
+ progress_bar.progress(1.0, text="All images generated!")
91
+ st.success("βœ… All images generated.")
92
+ return final_output
93
+
94
+ def run_youtube_analysis_workflow(yt_url: str):
95
+ """
96
+ Analyzes a YouTube video directly from its URL without downloading.
97
+ """
98
+ st.write("### Step 1: Analyzing YouTube Video...")
99
+ st.info("Sending URL to Gemini for analysis...")
100
+
101
+ try:
102
+ # The key is to use a tool that tells the model how to handle the YouTube URL.
103
+ from google.ai.generativelanguage import Part, FileData
104
+
105
+ video_analysis_model = genai.GenerativeModel('models/gemini-1.5-flash-latest')
106
+
107
+ prompt = "Analyze this video then compile the top 2-3 core topics and then write a detailed summary on it. Maintain the original order of events."
108
+
109
+ # Create the FileData part using the YouTube URI
110
+ video_part = Part(
111
+ file_data=FileData(
112
+ mime_type="video/youtube",
113
+ file_uri=yt_url
114
+ )
115
+ )
116
+
117
+ with st.spinner("Gemini is processing the video... This may take a few minutes for longer videos."):
118
+ response = video_analysis_model.generate_content([prompt, video_part], request_options={"timeout": 600})
119
+
120
+ video_summary = response.text
121
+ st.success("βœ… YouTube video analysis complete.")
122
+ with st.expander("See Raw Video Summary"):
123
+ st.markdown(video_summary)
124
+
125
+ # Now, pass the summary to the shared enrichment/image generation stage
126
+ return enrich_and_generate_images(video_summary)
127
+
128
+ except Exception as e:
129
+ st.error(f"An error occurred during YouTube analysis: {e}")
130
+ st.info("This can sometimes happen with private, age-restricted, or very new videos. Please check the URL and try again.")
131
+ return None
132
+
133
+ def run_local_file_analysis_workflow(video_path: str):
134
+ """
135
+ Analyzes a video from a local file path by uploading it to Google.
136
+ """
137
+ video_file_obj = None
138
+ try:
139
+ st.write("### Step 1: Uploading & Analyzing Local Video File...")
140
+ st.info("Uploading video file to Google. This may take a moment...")
141
+ video_file_obj = genai.upload_file(path=video_path)
142
+ st.info(f"Video uploaded: {video_file_obj.name}. Waiting for processing...")
143
+
144
+ while video_file_obj.state.name == "PROCESSING":
145
+ time.sleep(5)
146
+ video_file_obj = genai.get_file(video_file_obj.name)
147
+
148
+ if video_file_obj.state.name == "FAILED":
149
+ st.error(f"Video processing failed: {video_file_obj.state}")
150
+ return None
151
+
152
+ st.success("βœ… Video is processed and ready for analysis.")
153
+
154
+ video_analysis_model = genai.GenerativeModel('models/gemini-1.5-flash-latest')
155
+ prompt = ("Analyze this video in detail. First, identify the 2-3 core topics discussed. "
156
+ "Then, write a comprehensive summary of the video's content, maintaining the original order of events.")
157
+
158
+ response = video_analysis_model.generate_content([prompt, video_file_obj], request_options={"timeout": 600})
159
+ video_summary = response.text
160
+ st.success("βœ… Local video analysis complete.")
161
+ with st.expander("See Raw Video Summary"):
162
+ st.markdown(video_summary)
163
+
164
+ # Pass the summary to the shared enrichment/image generation stage
165
+ return enrich_and_generate_images(video_summary)
166
+
167
+ except Exception as e:
168
+ st.error(f"An error occurred during local file analysis: {e}")
169
+ return None
170
+ finally:
171
+ if video_file_obj:
172
+ genai.delete_file(video_file_obj.name)
173
+ st.info(f"Cleaned up cloud file: {video_file_obj.name}")
174
+
175
+
176
+ # --- Streamlit UI ---
177
+
178
+ st.divider()
179
+ st.subheader("Select Video Source")
180
+
181
+ # Use tabs for a clean UI. We recommend YouTube for speed and reliability.
182
+ tab1, tab2 = st.tabs(["πŸ”— **Use a YouTube Link (Recommended)**", "πŸ“€ Upload a Video File"])
183
+ video_path = None
184
+ final_response = None
185
+
186
+ with tab1:
187
+ yt_url = st.text_input(
188
+ "Enter YouTube Video URL",
189
+ placeholder="https://www.youtube.com/watch?v=..."
190
+ )
191
+
192
+ with tab2:
193
+ video_file = st.file_uploader(
194
+ "Choose a video file",
195
+ type=["mp4", "mov", "avi", "mpeg", "webm"]
196
+ )
197
+
198
+ if st.button("πŸš€ Analyze and Generate Content", use_container_width=True):
199
+ # Prioritize YouTube URL if both are provided
200
+ if yt_url:
201
+ final_response = run_youtube_analysis_workflow(yt_url)
202
+ elif video_file:
203
+ with st.spinner("Saving uploaded file locally..."):
204
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") as tmp_file:
205
+ tmp_file.write(video_file.getbuffer())
206
+ video_path = tmp_file.name
207
+
208
+ try:
209
+ final_response = run_local_file_analysis_workflow(video_path)
210
+ finally:
211
+ if video_path and os.path.exists(video_path):
212
+ os.remove(video_path)
213
+ st.info(f"Cleaned up local temporary file: {video_path}")
214
+ else:
215
+ st.warning("Please provide a YouTube URL or upload a video file.")
216
+ st.stop()
217
+
218
+ # Display the final generated content if the workflow was successful
219
+ if final_response:
220
+ st.divider()
221
+ st.markdown("## ✨ Final Generated Content")
222
+ st.markdown(final_response, unsafe_allow_html=True)
223
+ else:
224
  st.error("The analysis workflow did not produce a final output. Please check the errors above.")