stevafernandes commited on
Commit
319aac2
Β·
verified Β·
1 Parent(s): f0344bc

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +137 -143
app.py CHANGED
@@ -10,14 +10,10 @@ def get_api_key():
10
  # First try to get from Hugging Face Spaces secrets
11
  GOOGLE_API_KEY = os.environ.get("GOOGLE_API_KEY", "")
12
 
13
- # Fallback to Streamlit secrets if available
14
- if not GOOGLE_API_KEY and hasattr(st, "secrets"):
15
- GOOGLE_API_KEY = st.secrets.get("GOOGLE_API_KEY", "")
16
-
17
- # Last resort: manual input (remove in production)
18
  if not GOOGLE_API_KEY:
19
  GOOGLE_API_KEY = st.sidebar.text_input(
20
- "Enter Gemini API key (for testing only)",
21
  type="password",
22
  help="In production, this should be set as a Hugging Face Space secret"
23
  )
@@ -44,11 +40,19 @@ class VideoProcessor:
44
  raise e
45
 
46
  def wait_for_processing(self, video_file):
47
- while video_file.state.name == "PROCESSING":
 
 
 
48
  time.sleep(2)
49
  video_file = genai.get_file(video_file.name)
 
 
50
  if video_file.state.name == "FAILED":
51
  raise RuntimeError("Video processing failed")
 
 
 
52
  return video_file
53
 
54
  def generate_summary(self, video_file):
@@ -63,12 +67,11 @@ class VideoProcessor:
63
 
64
  Please format the summary in a clear, structured way."""
65
 
66
- response = self.model.generate_content([video_file, prompt])
67
- return response.text
68
-
69
- def chat_with_video(self, video_file, prompt):
70
- response = self.model.generate_content([video_file, prompt])
71
- return response.text
72
 
73
  # Initialize session state
74
  if "video_processor" not in st.session_state:
@@ -77,10 +80,10 @@ if "video_file" not in st.session_state:
77
  st.session_state.video_file = None
78
  if "summary" not in st.session_state:
79
  st.session_state.summary = None
80
- if "messages" not in st.session_state:
81
- st.session_state.messages = []
82
  if "video_name" not in st.session_state:
83
  st.session_state.video_name = None
 
 
84
 
85
  # Main app function
86
  def main():
@@ -90,41 +93,82 @@ def main():
90
  layout="wide"
91
  )
92
 
93
- st.title("πŸŽ₯ Video Upload & AI Analysis")
94
- st.markdown("Upload a video or record one to get AI-powered insights")
 
95
  st.markdown("---")
96
 
97
- # Get API key
98
- api_key = get_api_key()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
 
 
100
  if not api_key:
101
- st.error("⚠️ Please set your GOOGLE_API_KEY in Hugging Face Spaces secrets or environment variables")
102
  st.info("""
103
- To set up the API key in Hugging Face Spaces:
104
- 1. Go to your Space Settings
105
- 2. Navigate to 'Repository secrets'
106
- 3. Add a new secret named 'GOOGLE_API_KEY'
107
- 4. Paste your Gemini API key as the value
 
108
  """)
109
  st.stop()
110
 
111
  # Initialize video processor
112
  if st.session_state.video_processor is None:
113
- st.session_state.video_processor = VideoProcessor(api_key)
 
 
 
 
114
 
115
- # Create tabs for different input methods
116
- tab1, tab2, tab3 = st.tabs(["πŸ“€ Upload Video", "🎬 Record Video (Mobile)", "πŸ’¬ Chat with Video"])
117
 
118
  with tab1:
119
  st.subheader("Upload a video file")
120
 
121
  uploaded_file = st.file_uploader(
122
  "Choose a video file",
123
- type=['mp4', 'mov', 'avi', 'mkv', 'webm', 'm4v'],
124
- help="Maximum recommended size: 100MB"
125
  )
126
 
127
  if uploaded_file is not None:
 
 
 
 
128
  # Display video
129
  st.video(uploaded_file)
130
 
@@ -133,70 +177,71 @@ def main():
133
  st.session_state.video_name = uploaded_file.name
134
  st.session_state.video_file = None
135
  st.session_state.summary = None
136
- st.session_state.messages = []
137
-
138
- col1, col2 = st.columns(2)
139
 
140
- with col1:
141
- if st.button("πŸ” Analyze Video", type="primary", use_container_width=True):
142
- with st.spinner("Processing video... This may take a minute."):
143
- try:
144
- # Upload and process video
 
 
 
 
 
145
  video_bytes = uploaded_file.read()
146
  video_file, tmp_path = st.session_state.video_processor.upload_video(
147
  video_bytes,
148
  uploaded_file.name
149
  )
150
-
151
- # Wait for processing
152
  processed_file = st.session_state.video_processor.wait_for_processing(
153
  video_file
154
  )
155
  st.session_state.video_file = processed_file
156
-
157
- # Generate summary
158
  st.session_state.summary = st.session_state.video_processor.generate_summary(
159
  processed_file
160
  )
161
-
162
- # Clean up
 
163
  os.unlink(tmp_path)
164
-
165
- st.success("βœ… Analysis complete!")
166
-
167
- except Exception as e:
168
- st.error(f"Error processing video: {str(e)}")
169
-
170
- with col2:
171
- if st.button("πŸ”„ Reset", use_container_width=True):
172
- st.session_state.video_file = None
173
- st.session_state.summary = None
174
- st.session_state.messages = []
175
- st.session_state.video_name = None
176
  st.rerun()
177
 
178
  with tab2:
179
- st.subheader("Record a video (works best on mobile)")
180
 
181
  st.info("""
182
- πŸ“± **For Mobile Users:**
183
- Use the camera input below to record a video directly from your device.
184
 
185
- πŸ’» **For Desktop Users:**
186
- You may need to use the Upload tab instead, or record a video separately and upload it.
187
  """)
188
 
189
- # Use Streamlit's camera input for simple video recording
190
- video_file = st.camera_input("Record a video")
191
 
192
  if video_file is not None:
193
  st.video(video_file)
194
 
195
- if st.button("πŸ” Analyze Recorded Video", type="primary"):
196
- with st.spinner("Processing your recording..."):
197
- try:
198
- # Process the recorded video
 
 
 
199
  video_bytes = video_file.read()
 
 
200
  uploaded_video, tmp_path = st.session_state.video_processor.upload_video(
201
  video_bytes,
202
  f"recording_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4"
@@ -214,99 +259,48 @@ def main():
214
  )
215
 
216
  # Clean up
217
- os.unlink(tmp_path)
 
218
 
219
  st.success("βœ… Recording analyzed!")
220
 
221
- except Exception as e:
222
- st.error(f"Error processing recording: {str(e)}")
223
-
224
- with tab3:
225
- st.subheader("Chat about your video")
226
-
227
- if st.session_state.video_file:
228
- # Display chat messages
229
- for msg in st.session_state.messages:
230
- with st.chat_message(msg["role"]):
231
- st.markdown(msg["content"])
232
-
233
- # Chat input
234
- user_question = st.chat_input("Ask a question about the video...")
235
-
236
- if user_question:
237
- # Add user message
238
- st.session_state.messages.append({"role": "user", "content": user_question})
239
- with st.chat_message("user"):
240
- st.markdown(user_question)
241
-
242
- # Generate response
243
- with st.chat_message("assistant"):
244
- with st.spinner("Thinking..."):
245
- try:
246
- response = st.session_state.video_processor.chat_with_video(
247
- st.session_state.video_file,
248
- user_question
249
- )
250
- st.markdown(response)
251
- st.session_state.messages.append({"role": "assistant", "content": response})
252
- except Exception as e:
253
- st.error(f"Error generating response: {str(e)}")
254
- else:
255
- st.info("Please upload or record a video first, then analyze it to start chatting.")
256
 
257
- # Display summary if available
258
  if st.session_state.summary:
259
  st.markdown("---")
260
- st.subheader("πŸ“ Video Summary")
261
 
262
- # Create columns for better layout
263
- col1, col2 = st.columns([3, 1])
264
 
265
  with col1:
266
- st.markdown(st.session_state.summary)
 
 
267
 
268
  with col2:
 
269
  st.download_button(
270
- label="πŸ“₯ Download Summary",
271
  data=st.session_state.summary,
272
- file_name=f"video_summary_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt",
273
  mime="text/plain",
274
  use_container_width=True
275
  )
276
 
277
- # Sidebar with instructions and info
278
- with st.sidebar:
279
- st.markdown("### πŸ“– How to Use")
280
- st.markdown("""
281
- 1. **Set API Key**: Add your Gemini API key to HF Spaces secrets
282
- 2. **Upload/Record**: Choose a video file or record one
283
- 3. **Analyze**: Click the analyze button
284
- 4. **Review**: Read the AI-generated summary
285
- 5. **Chat**: Ask questions about the video content
286
- """)
287
-
288
- st.markdown("### 🎯 Best Practices")
289
- st.markdown("""
290
- - Keep videos under 100MB for faster processing
291
- - Ensure good lighting for recordings
292
- - Speak clearly if recording audio
293
- - Videos with clear content work best
294
- """)
295
-
296
- st.markdown("### βš™οΈ System Status")
297
- if api_key:
298
- st.success("βœ… API Key configured")
299
- else:
300
- st.error("❌ API Key missing")
301
-
302
- if st.session_state.video_file:
303
- st.success("βœ… Video loaded")
304
- else:
305
- st.info("⏳ No video loaded")
306
-
307
- if st.button("πŸ”„ Reset Everything"):
308
- st.session_state.clear()
309
- st.rerun()
310
 
311
  if __name__ == "__main__":
312
  main()
 
10
  # First try to get from Hugging Face Spaces secrets
11
  GOOGLE_API_KEY = os.environ.get("GOOGLE_API_KEY", "")
12
 
13
+ # Fallback to manual input for testing
 
 
 
 
14
  if not GOOGLE_API_KEY:
15
  GOOGLE_API_KEY = st.sidebar.text_input(
16
+ "Enter Gemini API key",
17
  type="password",
18
  help="In production, this should be set as a Hugging Face Space secret"
19
  )
 
40
  raise e
41
 
42
  def wait_for_processing(self, video_file):
43
+ max_attempts = 30 # Maximum 60 seconds wait
44
+ attempts = 0
45
+
46
+ while video_file.state.name == "PROCESSING" and attempts < max_attempts:
47
  time.sleep(2)
48
  video_file = genai.get_file(video_file.name)
49
+ attempts += 1
50
+
51
  if video_file.state.name == "FAILED":
52
  raise RuntimeError("Video processing failed")
53
+ elif video_file.state.name == "PROCESSING":
54
+ raise RuntimeError("Video processing timeout")
55
+
56
  return video_file
57
 
58
  def generate_summary(self, video_file):
 
67
 
68
  Please format the summary in a clear, structured way."""
69
 
70
+ try:
71
+ response = self.model.generate_content([video_file, prompt])
72
+ return response.text
73
+ except Exception as e:
74
+ return f"Error generating summary: {str(e)}"
 
75
 
76
  # Initialize session state
77
  if "video_processor" not in st.session_state:
 
80
  st.session_state.video_file = None
81
  if "summary" not in st.session_state:
82
  st.session_state.summary = None
 
 
83
  if "video_name" not in st.session_state:
84
  st.session_state.video_name = None
85
+ if "processing" not in st.session_state:
86
+ st.session_state.processing = False
87
 
88
  # Main app function
89
  def main():
 
93
  layout="wide"
94
  )
95
 
96
+ # Header
97
+ st.title("πŸŽ₯ Video Recording & AI Analysis")
98
+ st.markdown("Upload or record a video to get instant AI-powered insights")
99
  st.markdown("---")
100
 
101
+ # Sidebar for API key and info
102
+ with st.sidebar:
103
+ st.markdown("### πŸ”‘ Configuration")
104
+
105
+ # Get API key
106
+ api_key = get_api_key()
107
+
108
+ if api_key:
109
+ st.success("βœ… API Key configured")
110
+ else:
111
+ st.error("❌ API Key missing")
112
+
113
+ st.markdown("---")
114
+ st.markdown("### πŸ“– How to Use")
115
+ st.markdown("""
116
+ 1. Add API key (in HF secrets)
117
+ 2. Upload or record a video
118
+ 3. Click 'Analyze Video'
119
+ 4. View AI-generated summary
120
+ """)
121
+
122
+ st.markdown("### 🎯 Tips")
123
+ st.markdown("""
124
+ - Keep videos under 50MB
125
+ - Clear content works best
126
+ - Good lighting helps
127
+ """)
128
+
129
+ if st.button("πŸ”„ Reset Everything"):
130
+ for key in list(st.session_state.keys()):
131
+ del st.session_state[key]
132
+ st.rerun()
133
 
134
+ # Check API key
135
  if not api_key:
136
+ st.error("⚠️ Please enter your GOOGLE_API_KEY in the sidebar")
137
  st.info("""
138
+ **For Hugging Face Spaces deployment:**
139
+ 1. Go to Settings β†’ Repository secrets
140
+ 2. Add secret named 'GOOGLE_API_KEY'
141
+ 3. Paste your Gemini API key value
142
+
143
+ **Get API key from:** https://makersuite.google.com/app/apikey
144
  """)
145
  st.stop()
146
 
147
  # Initialize video processor
148
  if st.session_state.video_processor is None:
149
+ try:
150
+ st.session_state.video_processor = VideoProcessor(api_key)
151
+ except Exception as e:
152
+ st.error(f"Error initializing processor: {str(e)}")
153
+ st.stop()
154
 
155
+ # Create tabs
156
+ tab1, tab2 = st.tabs(["πŸ“€ Upload Video", "πŸ“Ή Record Video"])
157
 
158
  with tab1:
159
  st.subheader("Upload a video file")
160
 
161
  uploaded_file = st.file_uploader(
162
  "Choose a video file",
163
+ type=['mp4', 'mov', 'avi', 'mkv', 'webm'],
164
+ help="Maximum recommended size: 50MB"
165
  )
166
 
167
  if uploaded_file is not None:
168
+ # Display video info
169
+ file_size = len(uploaded_file.getvalue()) / (1024 * 1024)
170
+ st.info(f"πŸ“ File: {uploaded_file.name} | Size: {file_size:.2f} MB")
171
+
172
  # Display video
173
  st.video(uploaded_file)
174
 
 
177
  st.session_state.video_name = uploaded_file.name
178
  st.session_state.video_file = None
179
  st.session_state.summary = None
 
 
 
180
 
181
+ # Analyze button
182
+ if st.button("πŸ” Analyze Video", type="primary", disabled=st.session_state.processing):
183
+ st.session_state.processing = True
184
+
185
+ try:
186
+ # Create progress container
187
+ progress_container = st.container()
188
+
189
+ with progress_container:
190
+ with st.spinner("πŸ“€ Uploading video..."):
191
  video_bytes = uploaded_file.read()
192
  video_file, tmp_path = st.session_state.video_processor.upload_video(
193
  video_bytes,
194
  uploaded_file.name
195
  )
196
+
197
+ with st.spinner("⏳ Processing video... (this may take 30-60 seconds)"):
198
  processed_file = st.session_state.video_processor.wait_for_processing(
199
  video_file
200
  )
201
  st.session_state.video_file = processed_file
202
+
203
+ with st.spinner("πŸ€– Generating AI summary..."):
204
  st.session_state.summary = st.session_state.video_processor.generate_summary(
205
  processed_file
206
  )
207
+
208
+ # Clean up
209
+ if os.path.exists(tmp_path):
210
  os.unlink(tmp_path)
211
+
212
+ st.success("βœ… Analysis complete!")
213
+
214
+ except Exception as e:
215
+ st.error(f"❌ Error: {str(e)}")
216
+ finally:
217
+ st.session_state.processing = False
 
 
 
 
 
218
  st.rerun()
219
 
220
  with tab2:
221
+ st.subheader("Record a video")
222
 
223
  st.info("""
224
+ πŸ“± **Mobile Users:** Use the camera button below to record
 
225
 
226
+ πŸ’» **Desktop Users:** This feature works best on mobile devices
 
227
  """)
228
 
229
+ # Camera input for recording
230
+ video_file = st.camera_input("Click to record video")
231
 
232
  if video_file is not None:
233
  st.video(video_file)
234
 
235
+ if st.button("πŸ” Analyze Recording", type="primary", disabled=st.session_state.processing):
236
+ st.session_state.processing = True
237
+
238
+ try:
239
+ with st.spinner("Processing your recording..."):
240
+ # Reset video file pointer
241
+ video_file.seek(0)
242
  video_bytes = video_file.read()
243
+
244
+ # Upload and process
245
  uploaded_video, tmp_path = st.session_state.video_processor.upload_video(
246
  video_bytes,
247
  f"recording_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4"
 
259
  )
260
 
261
  # Clean up
262
+ if os.path.exists(tmp_path):
263
+ os.unlink(tmp_path)
264
 
265
  st.success("βœ… Recording analyzed!")
266
 
267
+ except Exception as e:
268
+ st.error(f"❌ Error: {str(e)}")
269
+ finally:
270
+ st.session_state.processing = False
271
+ st.rerun()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
272
 
273
+ # Display summary
274
  if st.session_state.summary:
275
  st.markdown("---")
276
+ st.subheader("πŸ“ AI-Generated Video Summary")
277
 
278
+ # Create columns for layout
279
+ col1, col2 = st.columns([4, 1])
280
 
281
  with col1:
282
+ # Display summary in a nice container
283
+ with st.container():
284
+ st.markdown(st.session_state.summary)
285
 
286
  with col2:
287
+ # Download button
288
  st.download_button(
289
+ label="πŸ“₯ Download",
290
  data=st.session_state.summary,
291
+ file_name=f"summary_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt",
292
  mime="text/plain",
293
  use_container_width=True
294
  )
295
 
296
+ # Footer
297
+ st.markdown("---")
298
+ st.markdown(
299
+ "<div style='text-align: center; color: gray; font-size: 0.8em;'>"
300
+ "Powered by Google Gemini 2.0 Flash | Made with Streamlit"
301
+ "</div>",
302
+ unsafe_allow_html=True
303
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
304
 
305
  if __name__ == "__main__":
306
  main()