mustafoyev202 commited on
Commit
c9440c7
·
verified ·
1 Parent(s): d385097

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +139 -0
app.py ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import gradio as gr
3
+ from google import genai
4
+ import pandas as pd
5
+ import os
6
+ import re
7
+ import concurrent.futures
8
+ from dotenv import load_dotenv
9
+
10
+ # Load environment variables from .env file
11
+ load_dotenv()
12
+
13
+ # Initialize the GenAI client with the API key
14
+ client = genai.Client(api_key=os.getenv("GOOGLE_API_KEY"))
15
+
16
+ def analyze_single_video(video_path):
17
+ """Analyzes a single video for emotions using the GenAI model."""
18
+ prompt = """
19
+ Detect emotion from this video and classify into 3 categories: happy, sad, normal. Return only JSON format without any extra text.
20
+
21
+ Return this JSON schema:
22
+
23
+ {
24
+ "Vocal": {
25
+ "sad_score": (%),
26
+ "happy_score": (%),
27
+ "normal_score": (%),
28
+ "sad_reason": (list of timestamps),
29
+ "happy_reason": (list of timestamps),
30
+ "normal_reason": (list of timestamps)
31
+ },
32
+ "Verbal": {
33
+ "sad_score": (%),
34
+ "happy_score": (%),
35
+ "normal_score": (%),
36
+ "sad_reason": (list of timestamps),
37
+ "happy_reason": (list of timestamps),
38
+ "normal_reason": (list of timestamps)
39
+ },
40
+ "Vision": {
41
+ "sad_score": (%),
42
+ "happy_score": (%),
43
+ "normal_score": (%),
44
+ "sad_reason": (list of timestamps),
45
+ "happy_reason": (list of timestamps),
46
+ "normal_reason": (list of timestamps)
47
+ }
48
+ }
49
+
50
+ Reasons (sad_reason, happy_reason, normal_reason) should be a list of beginning-ending timestamps. For example: ['0:11-0:14', '0:23-0:25', '0:27-0:29']
51
+ """
52
+
53
+ try:
54
+ with open(video_path, 'rb') as video_file:
55
+ video_bytes = video_file.read()
56
+
57
+ print(f"Processing: {video_path}")
58
+
59
+ response = client.models.generate_content(
60
+ model="gemini-2.0-flash",
61
+ contents=[{"text": prompt}, {"inline_data": {"data": video_bytes, "mime_type": "video/mp4"}}],
62
+ config={"http_options": {"timeout": 60000}}
63
+ )
64
+
65
+ response_text = response.text.strip()
66
+ json_match = re.search(r'```json\s*([\s\S]*?)\s*```', response_text)
67
+ json_string = json_match.group(1).strip() if json_match else response_text
68
+ result = json.loads(json_string)
69
+
70
+ return result
71
+
72
+ except Exception as e:
73
+ print(f"Error processing {video_path}: {e}")
74
+ return None
75
+
76
+ def process_multiple_videos(video_paths):
77
+ """Processes multiple videos and stores the emotion analysis results."""
78
+ records = []
79
+
80
+ with concurrent.futures.ThreadPoolExecutor() as executor:
81
+ results = list(executor.map(analyze_single_video, video_paths))
82
+
83
+ # Process results and organize them into a DataFrame
84
+ for video_path, result in zip(video_paths, results):
85
+ if result is None:
86
+ continue # Skip invalid results
87
+
88
+ video_title = os.path.basename(video_path)
89
+ print(f"Processing result for {video_title}: {result}")
90
+
91
+ try:
92
+ for category in ['Verbal', 'Vocal', 'Vision']:
93
+ for emotion in ['normal', 'happy', 'sad']:
94
+ score = result[category].get(f"{emotion}_score", 0)
95
+ reasons = result[category].get(f"{emotion}_reason", [])
96
+ records.append({
97
+ 'title': video_title,
98
+ 'category': category,
99
+ 'emotion': emotion,
100
+ 'score': score,
101
+ 'reasons': json.dumps(reasons) # Ensure reasons are serialized as JSON
102
+ })
103
+ except KeyError as e:
104
+ print(f"Skipping invalid result for {video_title} due to missing key: {e}")
105
+
106
+ # Create a DataFrame and export to CSV and Excel
107
+ df = pd.DataFrame(records)
108
+ df.to_csv("emotion_results.csv", index=False)
109
+ df.to_excel("emotion_results.xlsx", index=False)
110
+ return df
111
+
112
+ def gradio_interface(video_paths):
113
+ """Handles the Gradio interface and video processing."""
114
+ # Filter valid .mp4 video files
115
+ paths = [file.name if hasattr(file, 'name') else file for file in video_paths]
116
+ paths = [p for p in paths if os.path.isfile(p) and p.endswith(".mp4")]
117
+
118
+ if not paths:
119
+ raise ValueError("No valid video files were provided.")
120
+
121
+ df = process_multiple_videos(paths)
122
+
123
+ # Save the DataFrame as CSV and return it
124
+ csv_file = "emotion_results.csv"
125
+ df.to_csv(csv_file, index=False)
126
+
127
+ return df, csv_file
128
+
129
+ # Gradio interface definition
130
+ iface = gr.Interface(
131
+ fn=gradio_interface,
132
+ inputs=gr.File(file_types=[".mp4"], label="Upload one or more videos", file_count="multiple"),
133
+ outputs=[gr.DataFrame(), gr.File(label="Download CSV")],
134
+ title="Batch Video Emotion Analyzer",
135
+ description="Upload multiple videos to analyze their emotions across verbal, vocal, and visual channels."
136
+ )
137
+
138
+ # Launch the interface
139
+ iface.launch(share=True)