Factor Studios commited on
Commit
4352b30
·
verified ·
1 Parent(s): cfee44c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +220 -267
app.py CHANGED
@@ -1,267 +1,220 @@
1
- import os
2
- import json
3
- import time
4
- import threading
5
- from fastapi import FastAPI, HTTPException, BackgroundTasks
6
- from fastapi.middleware.cors import CORSMiddleware
7
- from fastapi.responses import JSONResponse, FileResponse
8
- import uvicorn
9
- from typing import Dict
10
- from pathlib import Path
11
- import subprocess
12
- from datetime import datetime
13
-
14
- import torch
15
-
16
-
17
- # Import from vision_analyzer (previously cursor_tracker)
18
- from vision_analyzer import (
19
- main_processing_loop,
20
- processing_status,
21
- ANALYSIS_OUTPUT_FOLDER, # Changed from CURSOR_TRACKING_OUTPUT_FOLDER
22
- log_message
23
- )
24
-
25
- # FastAPI App Definition
26
- app = FastAPI(title="Video Analysis API",
27
- description="API to access video frame analysis results",
28
- version="1.0.0")
29
-
30
- # Add CORS middleware to allow cross-origin requests
31
- app.add_middleware(
32
- CORSMiddleware,
33
- allow_origins=["*"], # Allows all origins
34
- allow_credentials=True,
35
- allow_methods=["*"], # Allows all methods
36
- allow_headers=["*"],
37
- )
38
-
39
- # Global variable to track if processing is running
40
- processing_thread = None
41
-
42
- def log_message(message):
43
- """Add a log message with timestamp"""
44
- timestamp = datetime.now().strftime("%H:%M:%S")
45
- log_entry = f"[{timestamp}] {message}"
46
- processing_status["logs"].append(log_entry)
47
-
48
- # Keep only the last 100 logs
49
- if len(processing_status["logs"]) > 100:
50
- processing_status["logs"] = processing_status["logs"][-100:]
51
-
52
- print(log_entry)
53
-
54
- @app.on_event("startup")
55
- async def startup_event():
56
- """Run the processing loop in the background when the API starts"""
57
- global processing_thread
58
- if not (processing_thread and processing_thread.is_alive()):
59
- log_message("🚀 Starting RAR extraction, frame extraction, and vision analysis pipeline in background...")
60
- processing_thread = threading.Thread(target=main_processing_loop)
61
- processing_thread.daemon = True
62
- processing_thread.start()
63
-
64
- @app.get("/")
65
- async def root():
66
- """Root endpoint that returns basic info"""
67
- return {
68
- "message": "Video Analysis API",
69
- "status": "running",
70
- "endpoints": {
71
- "/status": "Get processing status",
72
- "/analysis-data": "List available analysis files",
73
- "/analysis-data/{filename}": "Get specific analysis data",
74
- "/start-processing": "Start processing pipeline",
75
- "/stop-processing": "Stop processing pipeline"
76
- }
77
- }
78
-
79
- @app.get("/status")
80
- async def get_status():
81
- """Get current processing status"""
82
- return {
83
- "processing_status": processing_status,
84
- "analysis_folder": ANALYSIS_OUTPUT_FOLDER,
85
- "folder_exists": os.path.exists(ANALYSIS_OUTPUT_FOLDER)
86
- }
87
-
88
- @app.get("/analysis-data")
89
- async def list_analysis_data():
90
- """List all available analysis JSON files"""
91
- if not os.path.exists(ANALYSIS_OUTPUT_FOLDER):
92
- return {"files": [], "message": "Analysis output folder does not exist yet"}
93
-
94
- json_files = []
95
- for file in os.listdir(ANALYSIS_OUTPUT_FOLDER):
96
- if file.endswith(".json"):
97
- file_path = os.path.join(ANALYSIS_OUTPUT_FOLDER, file)
98
- file_stats = os.stat(file_path)
99
- json_files.append({
100
- "filename": file,
101
- "size_bytes": file_stats.st_size,
102
- "modified_time": time.ctime(file_stats.st_mtime),
103
- "download_url": f"/analysis-data/{file}"
104
- })
105
-
106
- return {
107
- "files": json_files,
108
- "total_files": len(json_files),
109
- "folder_path": ANALYSIS_OUTPUT_FOLDER
110
- }
111
-
112
- @app.get("/analysis-data/{filename}")
113
- async def get_analysis_data(filename: str):
114
- """Get specific analysis data by filename"""
115
- if not filename.endswith(".json"):
116
- raise HTTPException(status_code=400, detail="File must be a JSON file")
117
-
118
- file_path = os.path.join(ANALYSIS_OUTPUT_FOLDER, filename)
119
-
120
- if not os.path.exists(file_path):
121
- raise HTTPException(status_code=404, detail=f"File {filename} not found")
122
-
123
- try:
124
- with open(file_path, "r") as f:
125
- data = json.load(f)
126
-
127
- # Add metadata
128
- file_stats = os.stat(file_path)
129
-
130
- # Extract summary information
131
- frame_analyses = data.get("frame_analyses", [])
132
- summary = data.get("summary", {})
133
-
134
- response_data = {
135
- "filename": filename,
136
- "file_size_bytes": file_stats.st_size,
137
- "modified_time": time.ctime(file_stats.st_mtime),
138
- "total_frames": len(frame_analyses),
139
- "summary": summary,
140
- "frame_samples": frame_analyses[:5] # Return first 5 frames as samples
141
- }
142
-
143
- return response_data
144
-
145
- except json.JSONDecodeError:
146
- raise HTTPException(status_code=500, detail=f"Invalid JSON in file {filename}")
147
- except Exception as e:
148
- raise HTTPException(status_code=500, detail=f"Error reading file {filename}: {str(e)}")
149
-
150
- @app.get("/analysis-data/{filename}/full")
151
- async def get_full_analysis_data(filename: str):
152
- """Get the complete analysis data including all frames"""
153
- if not filename.endswith(".json"):
154
- raise HTTPException(status_code=400, detail="File must be a JSON file")
155
-
156
- file_path = os.path.join(ANALYSIS_OUTPUT_FOLDER, filename)
157
-
158
- if not os.path.exists(file_path):
159
- raise HTTPException(status_code=404, detail=f"File {filename} not found")
160
-
161
- try:
162
- with open(file_path, "r") as f:
163
- data = json.load(f)
164
-
165
- # Add metadata
166
- file_stats = os.stat(file_path)
167
- data["metadata"] = {
168
- "filename": filename,
169
- "file_size_bytes": file_stats.st_size,
170
- "modified_time": time.ctime(file_stats.st_mtime)
171
- }
172
-
173
- return data
174
-
175
- except json.JSONDecodeError:
176
- raise HTTPException(status_code=500, detail=f"Invalid JSON in file {filename}")
177
- except Exception as e:
178
- raise HTTPException(status_code=500, detail=f"Error reading file {filename}: {str(e)}")
179
-
180
- @app.post("/start-processing")
181
- async def start_processing(background_tasks: BackgroundTasks, start_index: int = 0):
182
- """Start the processing pipeline in the background"""
183
- global processing_thread
184
-
185
- if processing_thread and processing_thread.is_alive():
186
- return {"message": "Processing is already running", "status": "already_running"}
187
-
188
- if processing_status["is_running"]:
189
- return {"message": "Processing is already running", "status": "already_running"}
190
-
191
- # Start processing in a background thread
192
- processing_thread = threading.Thread(target=main_processing_loop, args=(start_index,))
193
- processing_thread.daemon = True
194
- processing_thread.start()
195
-
196
- return {"message": f"Processing started in background from index {start_index}", "status": "started"}
197
-
198
- @app.post("/stop-processing")
199
- async def stop_processing():
200
- """Stop the processing pipeline"""
201
- global processing_thread
202
-
203
- if not processing_status["is_running"] and (not processing_thread or not processing_thread.is_alive()):
204
- return {"message": "No processing is currently running", "status": "not_running"}
205
-
206
- # Note: This is a graceful stop request
207
- processing_status["is_running"] = False
208
-
209
- return {"message": "Stop signal sent to processing pipeline", "status": "stop_requested"}
210
-
211
- @app.get("/analysis-data/{filename}/summary")
212
- async def get_analysis_summary(filename: str):
213
- """Get a summary of the analysis data"""
214
- if not filename.endswith(".json"):
215
- raise HTTPException(status_code=400, detail="File must be a JSON file")
216
-
217
- file_path = os.path.join(ANALYSIS_OUTPUT_FOLDER, filename)
218
-
219
- if not os.path.exists(file_path):
220
- raise HTTPException(status_code=404, detail=f"File {filename} not found")
221
-
222
- try:
223
- with open(file_path, "r") as f:
224
- data = json.load(f)
225
-
226
- # Get basic statistics
227
- frame_analyses = data.get("frame_analyses", [])
228
- summary = data.get("summary", {})
229
-
230
- # Count frames with descriptions
231
- frames_with_descriptions = len([f for f in frame_analyses if f.get("description")])
232
-
233
- file_stats = os.stat(file_path)
234
-
235
- return {
236
- "filename": filename,
237
- "file_size_bytes": file_stats.st_size,
238
- "modified_time": time.ctime(file_stats.st_mtime),
239
- "total_frames": len(frame_analyses),
240
- "frames_with_descriptions": frames_with_descriptions,
241
- "summary": summary,
242
- "steps": summary.get("steps", []),
243
- "high_level_goal": summary.get("high_level_goal", ""),
244
- "final_goal": summary.get("final_goal", "")
245
- }
246
-
247
- except json.JSONDecodeError:
248
- raise HTTPException(status_code=500, detail=f"Invalid JSON in file {filename}")
249
- except Exception as e:
250
- raise HTTPException(status_code=500, detail=f"Error reading file {filename}: {str(e)}")
251
-
252
- if __name__ == "__main__":
253
- # Start the FastAPI server
254
- print("Starting Video Analysis FastAPI Server...")
255
- print("API Documentation will be available at: http://localhost:8000/docs")
256
- print("API Root endpoint: http://localhost:8000/")
257
-
258
- # Ensure the analysis output folder exists
259
- os.makedirs(ANALYSIS_OUTPUT_FOLDER, exist_ok=True)
260
-
261
- uvicorn.run(
262
- app,
263
- host="0.0.0.0",
264
- port=8000,
265
- log_level="info",
266
- reload=False # Set to False for production
267
- )
 
1
+ import gradio as gr
2
+ import os
3
+ import json
4
+ import time
5
+ import threading
6
+ from datetime import datetime
7
+
8
+ # Import from vision_analyzer (previously cursor_tracker)
9
+ from vision_analyzer import (
10
+ main_processing_loop,
11
+ processing_status,
12
+ ANALYSIS_OUTPUT_FOLDER,
13
+ log_message
14
+ )
15
+
16
+ # Global variable to track if processing is running
17
+ processing_thread = None
18
+
19
+ def start_processing_gradio(start_index: int = 0):
20
+ global processing_thread
21
+
22
+ if processing_thread and processing_thread.is_alive():
23
+ return "Processing is already running"
24
+
25
+ if processing_status["is_running"]:
26
+ return "Processing is already running"
27
+
28
+ # Start processing in a background thread
29
+ processing_thread = threading.Thread(target=main_processing_loop, args=(start_index,))
30
+ processing_thread.daemon = True
31
+ processing_thread.start()
32
+
33
+ return f"Processing started in background from index {start_index}"
34
+
35
+ def stop_processing_gradio():
36
+ global processing_thread
37
+
38
+ if not processing_status["is_running"] and (not processing_thread or not processing_thread.is_alive()):
39
+ return "No processing is currently running"
40
+
41
+ # Note: This is a graceful stop request
42
+ processing_status["is_running"] = False
43
+
44
+ return "Stop signal sent to processing pipeline"
45
+
46
+ def get_status_gradio():
47
+ return {
48
+ "processing_status": processing_status,
49
+ "analysis_folder": ANALYSIS_OUTPUT_FOLDER,
50
+ "folder_exists": os.path.exists(ANALYSIS_OUTPUT_FOLDER)
51
+ }
52
+
53
+ def list_analysis_data_gradio():
54
+ if not os.path.exists(ANALYSIS_OUTPUT_FOLDER):
55
+ return "Analysis output folder does not exist yet"
56
+
57
+ json_files = []
58
+ for file in os.listdir(ANALYSIS_OUTPUT_FOLDER):
59
+ if file.endswith(".json"):
60
+ file_path = os.path.join(ANALYSIS_OUTPUT_FOLDER, file)
61
+ file_stats = os.stat(file_path)
62
+ json_files.append({
63
+ "filename": file,
64
+ "size_bytes": file_stats.st_size,
65
+ "modified_time": time.ctime(file_stats.st_mtime),
66
+ })
67
+
68
+ return json.dumps({
69
+ "files": json_files,
70
+ "total_files": len(json_files),
71
+ "folder_path": ANALYSIS_OUTPUT_FOLDER
72
+ }, indent=2)
73
+
74
+ def get_analysis_data_gradio(filename: str):
75
+ if not filename.endswith(".json"):
76
+ return "File must be a JSON file"
77
+
78
+ file_path = os.path.join(ANALYSIS_OUTPUT_FOLDER, filename)
79
+
80
+ if not os.path.exists(file_path):
81
+ return f"File {filename} not found"
82
+
83
+ try:
84
+ with open(file_path, "r") as f:
85
+ data = json.load(f)
86
+
87
+ # Add metadata
88
+ file_stats = os.stat(file_path)
89
+
90
+ # Extract summary information
91
+ frame_analyses = data.get("frame_analyses", [])
92
+ summary = data.get("summary", {})
93
+
94
+ response_data = {
95
+ "filename": filename,
96
+ "file_size_bytes": file_stats.st_size,
97
+ "modified_time": time.ctime(file_stats.st_mtime),
98
+ "total_frames": len(frame_analyses),
99
+ "summary": summary,
100
+ "frame_samples": frame_analyses[:5] # Return first 5 frames as samples
101
+ }
102
+
103
+ return json.dumps(response_data, indent=2)
104
+
105
+ except json.JSONDecodeError:
106
+ return f"Invalid JSON in file {filename}"
107
+ except Exception as e:
108
+ return f"Error reading file {filename}: {str(e)}"
109
+
110
+ def get_full_analysis_data_gradio(filename: str):
111
+ if not filename.endswith(".json"):
112
+ return "File must be a JSON file"
113
+
114
+ file_path = os.path.join(ANALYSIS_OUTPUT_FOLDER, filename)
115
+
116
+ if not os.path.exists(file_path):
117
+ return f"File {filename} not found"
118
+
119
+ try:
120
+ with open(file_path, "r") as f:
121
+ data = json.load(f)
122
+
123
+ # Add metadata
124
+ file_stats = os.stat(file_path)
125
+ data["metadata"] = {
126
+ "filename": filename,
127
+ "file_size_bytes": file_stats.st_size,
128
+ "modified_time": time.ctime(file_stats.st_mtime)
129
+ }
130
+
131
+ return json.dumps(data, indent=2)
132
+
133
+ except json.JSONDecodeError:
134
+ return f"Invalid JSON in file {filename}"
135
+ except Exception as e:
136
+ return f"Error reading file {filename}: {str(e)}"
137
+
138
+ def get_analysis_summary_gradio(filename: str):
139
+ if not filename.endswith(".json"):
140
+ return "File must be a JSON file"
141
+
142
+ file_path = os.path.join(ANALYSIS_OUTPUT_FOLDER, filename)
143
+
144
+ if not os.path.exists(file_path):
145
+ return f"File {filename} not found"
146
+
147
+ try:
148
+ with open(file_path, "r") as f:
149
+ data = json.load(f)
150
+
151
+ # Get basic statistics
152
+ frame_analyses = data.get("frame_analyses", [])
153
+ summary = data.get("summary", {})
154
+
155
+ # Count frames with descriptions
156
+ frames_with_descriptions = len([f for f in frame_analyses if f.get("description")])
157
+
158
+ file_stats = os.stat(file_path)
159
+
160
+ return json.dumps({
161
+ "filename": filename,
162
+ "file_size_bytes": file_stats.st_size,
163
+ "modified_time": time.ctime(file_stats.st_mtime),
164
+ "total_frames": len(frame_analyses),
165
+ "frames_with_descriptions": frames_with_descriptions,
166
+ "summary": summary,
167
+ "steps": summary.get("steps", []),
168
+ "high_level_goal": summary.get("high_level_goal", ""),
169
+ "final_goal": summary.get("final_goal", "")
170
+ }, indent=2)
171
+
172
+ except json.JSONDecodeError:
173
+ return f"Invalid JSON in file {filename}"
174
+ except Exception as e:
175
+ return f"Error reading file {filename}: {str(e)}"
176
+
177
+ # Ensure the analysis output folder exists
178
+ os.makedirs(ANALYSIS_OUTPUT_FOLDER, exist_ok=True)
179
+
180
+ # Gradio Interface
181
+ with gr.Blocks() as demo:
182
+ gr.Markdown("# Video Analysis Interface")
183
+
184
+ with gr.Tab("Processing Control"):
185
+ gr.Markdown("## Start/Stop Video Processing")
186
+ start_index_input = gr.Number(label="Start Index", value=0, precision=0)
187
+ start_btn = gr.Button("Start Processing")
188
+ stop_btn = gr.Button("Stop Processing")
189
+ processing_output = gr.Textbox(label="Processing Status")
190
+
191
+ start_btn.click(start_processing_gradio, inputs=start_index_input, outputs=processing_output)
192
+ stop_btn.click(stop_processing_gradio, outputs=processing_output)
193
+
194
+ with gr.Tab("Analysis Data"):
195
+ gr.Markdown("## View Analysis Data")
196
+ list_data_btn = gr.Button("List All Analysis Files")
197
+ list_data_output = gr.JSON(label="Available Analysis Files")
198
+
199
+ list_data_btn.click(list_analysis_data_gradio, outputs=list_data_output)
200
+
201
+ gr.Markdown("### Get Specific Analysis Data")
202
+ filename_input = gr.Textbox(label="Filename (e.g., video_analysis.json)")
203
+ get_data_btn = gr.Button("Get Analysis Data (Summary)")
204
+ get_full_data_btn = gr.Button("Get Full Analysis Data")
205
+ get_summary_btn = gr.Button("Get Analysis Summary")
206
+ specific_data_output = gr.JSON(label="Analysis Data")
207
+
208
+ get_data_btn.click(get_analysis_data_gradio, inputs=filename_input, outputs=specific_data_output)
209
+ get_full_data_btn.click(get_full_analysis_data_gradio, inputs=filename_input, outputs=specific_data_output)
210
+ get_summary_btn.click(get_analysis_summary_gradio, inputs=filename_input, outputs=specific_data_output)
211
+
212
+ with gr.Tab("Current Status"):
213
+ gr.Markdown("## Current Processing Status")
214
+ status_btn = gr.Button("Refresh Status")
215
+ status_output = gr.JSON(label="Current Status")
216
+
217
+ status_btn.click(get_status_gradio, outputs=status_output)
218
+
219
+ if __name__ == "__main__":
220
+ demo.launch(server_name="0.0.0.0", server_port=7860)