项目文件夹

文件
2026-07-13 12:37:47 +08:00

296 行
10 KiB
Python

import streamlit as st
import google.generativeai as genai
import os
import tempfile
import time
import base64
from pathlib import Path
import mimetypes
from dotenv import load_dotenv
# Load environment variables
load_dotenv()
# Page configuration
st.set_page_config(
page_title="Video RAG with Gemini",
page_icon="🎬",
layout="wide",
initial_sidebar_state="expanded"
)
# ===========================
# Video Processing Class
# ===========================
class VideoProcessor:
def __init__(self, api_key):
genai.configure(api_key=api_key)
self.model = genai.GenerativeModel('gemini-1.5-pro')
def upload_video(self, video_path, display_name=None):
"""Upload video to Gemini File API"""
try:
video_file = genai.upload_file(
path=video_path,
display_name=display_name or "uploaded_video"
)
return video_file
except Exception as e:
st.error(f"Error uploading video: {str(e)}")
return None
def wait_for_file_processing(self, video_file):
"""Wait for video file to be processed by Gemini"""
try:
while video_file.state.name == "PROCESSING":
time.sleep(2)
video_file = genai.get_file(video_file.name)
if video_file.state.name == "FAILED":
raise ValueError("Video processing failed")
return video_file
except Exception as e:
st.error(f"Error processing video: {str(e)}")
return None
def chat_with_video(self, video_file, prompt):
"""Generate response based on video content and user prompt"""
try:
response = self.model.generate_content([
video_file,
prompt
])
return response.text
except Exception as e:
st.error(f"Error generating response: {str(e)}")
return None
# ===========================
# Helper Functions
# ===========================
def is_video_file(file):
"""Check if uploaded file is a video"""
if file is None:
return False
mime_type, _ = mimetypes.guess_type(file.name)
return mime_type and mime_type.startswith('video/')
def get_file_size_mb(file):
"""Get file size in MB"""
return len(file.getvalue()) / (1024 * 1024)
def reset_chat():
"""Reset chat history and video state"""
st.session_state.messages = []
if 'video_file' in st.session_state:
try:
# Clean up uploaded file from Gemini
genai.delete_file(st.session_state.video_file.name)
except:
pass
del st.session_state.video_file
if 'video_processor' in st.session_state:
del st.session_state.video_processor
if 'video_name' in st.session_state:
del st.session_state.video_name
def display_video(video_bytes, video_name):
"""Display uploaded video"""
st.markdown(f"### 🎬 {video_name}")
st.video(video_bytes)
# ===========================
# Session State Initialization
# ===========================
if "messages" not in st.session_state:
st.session_state.messages = []
if "video_file" not in st.session_state:
st.session_state.video_file = None
if "video_processor" not in st.session_state:
st.session_state.video_processor = None
if "video_name" not in st.session_state:
st.session_state.video_name = None
# ===========================
# Sidebar Configuration
# ===========================
with st.sidebar:
st.header("🔑 API Configuration")
# Try to get API key from environment first
default_api_key = os.getenv("GEMINI_API_KEY", "")
api_key = st.text_input(
"Gemini API Key",
value=default_api_key,
type="password",
help="Get your API key from https://aistudio.google.com/app/apikey"
)
if api_key:
if st.session_state.video_processor is None:
st.session_state.video_processor = VideoProcessor(api_key)
st.markdown("---")
st.header("📹 Upload Video")
uploaded_file = st.file_uploader(
"Choose a video file",
type=['mp4', 'avi', 'mov', 'mkv', 'webm'],
help="Supported formats: MP4, AVI, MOV, MKV, WEBM"
)
if uploaded_file is not None:
if not is_video_file(uploaded_file):
st.error("Please upload a valid video file.")
else:
file_size = get_file_size_mb(uploaded_file)
st.info(f"File size: {file_size:.2f} MB")
if file_size > 100: # Gemini has file size limits
st.warning("Large files may take longer to process or may fail. Consider compressing your video.")
# Process video if not already processed
if (st.session_state.video_file is None or
st.session_state.video_name != uploaded_file.name):
if st.session_state.video_processor is None:
st.error("Please enter your Gemini API key first.")
else:
with st.spinner("Uploading and processing video... This may take a few minutes."):
# Save uploaded file temporarily
with tempfile.NamedTemporaryFile(delete=False, suffix=Path(uploaded_file.name).suffix) as tmp_file:
tmp_file.write(uploaded_file.getvalue())
tmp_file_path = tmp_file.name
try:
# Upload to Gemini
video_file = st.session_state.video_processor.upload_video(
tmp_file_path,
uploaded_file.name
)
if video_file:
# Wait for processing
processed_file = st.session_state.video_processor.wait_for_file_processing(video_file)
if processed_file:
st.session_state.video_file = processed_file
st.session_state.video_name = uploaded_file.name
st.success("✅ Video processed successfully!")
# Reset chat when new video is uploaded
st.session_state.messages = []
finally:
# Clean up temporary file
try:
os.unlink(tmp_file_path)
except:
pass
# Display video preview
if st.session_state.video_file:
display_video(uploaded_file.getvalue(), uploaded_file.name)
st.markdown("---")
# Control buttons
col1, col2 = st.columns(2)
with col1:
if st.button("🗑️ Clear Chat"):
st.session_state.messages = []
st.rerun()
with col2:
if st.button("🔄 Reset All"):
reset_chat()
st.rerun()
# ===========================
# Main Chat Interface
# ===========================
st.title("🎬 Video RAG with Gemini")
st.markdown("Upload a video and chat with it using Google's Gemini AI!")
# Check if ready to chat
if not api_key:
st.info("👈 Please enter your Gemini API key in the sidebar to get started.")
elif st.session_state.video_file is None:
st.info("👈 Please upload a video file in the sidebar to start chatting.")
else:
st.success(f"✅ Ready to chat about: **{st.session_state.video_name}**")
# Display chat history
for message in st.session_state.messages:
with st.chat_message(message["role"]):
st.markdown(message["content"])
# Example prompts
if len(st.session_state.messages) == 0:
st.markdown("### 💡 Try these example questions:")
example_prompts = [
"What is happening in this video?",
"Summarize the main events",
"Describe the people and objects you see",
"What is the setting or environment?",
"What actions are taking place?"
]
cols = st.columns(2)
for i, example in enumerate(example_prompts):
with cols[i % 2]:
if st.button(f"💬 {example}", key=f"example_{i}"):
# Simulate user input
st.session_state.messages.append({"role": "user", "content": example})
st.rerun()
# Chat input
if prompt := st.chat_input("Ask a question about your video..."):
# Add user message to chat history
st.session_state.messages.append({"role": "user", "content": prompt})
with st.chat_message("user"):
st.markdown(prompt)
# Generate and display assistant response
with st.chat_message("assistant"):
message_placeholder = st.empty()
with st.spinner("Analyzing video and generating response..."):
response = st.session_state.video_processor.chat_with_video(
st.session_state.video_file,
prompt
)
if response:
# Simulate streaming effect
full_response = ""
for chunk in response.split():
full_response += chunk + " "
message_placeholder.markdown(full_response + "▌")
time.sleep(0.05)
# Final response without cursor
message_placeholder.markdown(response)
# Add assistant response to chat history
st.session_state.messages.append({"role": "assistant", "content": response})
else:
st.error("Failed to generate response. Please try again.")
# ===========================
# Footer
# ===========================
st.markdown("---")
st.markdown("""
<div style='text-align: center; color: #666;'>
<p>Built with ❤️ using Gemini API and Streamlit |
<a href='https://ai.google.dev/gemini-api/docs/video-understanding' target='_blank'>Learn more about Gemini Video API</a></p>
</div>
""", unsafe_allow_html=True)