Vennilavan commited on
Commit
19f34bb
·
verified ·
1 Parent(s): 631eb6a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +217 -207
app.py CHANGED
@@ -1,207 +1,217 @@
1
- from fastapi import FastAPI, UploadFile, File, HTTPException, Query
2
- from fastapi.middleware.cors import CORSMiddleware
3
- import uvicorn
4
- import os
5
- import tempfile
6
- import aiofiles
7
- from datetime import datetime
8
- import traceback
9
- import logging
10
- from typing import List, Dict, Any
11
- import httpx
12
-
13
- # Setup logging
14
- logging.basicConfig(level=logging.INFO)
15
- logger = logging.getLogger(__name__)
16
-
17
- app = FastAPI(title="Video Summarizer API")
18
-
19
- # Load environment variables
20
- import os
21
- from dotenv import load_dotenv
22
- load_dotenv()
23
-
24
- # Get URLs from environment
25
- FRONTEND_URL = os.getenv('FRONTEND_URL')
26
- BACKEND_URL = os.getenv('BACKEND_URL', 'http://localhost:5000')
27
-
28
- # CORS middleware
29
- app.add_middleware(
30
- CORSMiddleware,
31
- allow_origins=[FRONTEND_URL, BACKEND_URL],
32
- allow_credentials=True,
33
- allow_methods=["*"],
34
- allow_headers=["*"],
35
- )
36
-
37
- # Import processing functions with error handling
38
- try:
39
- from transcriber import extract_audio, transcribe_audio
40
- from summarizer import summarize_text
41
- from recommendation import recommend_courses
42
- from utils import chunked_summarize
43
- DEPENDENCIES_LOADED = True
44
- logger.info("All AI dependencies loaded successfully")
45
- except ImportError as e:
46
- logger.error(f"Import error: {e}")
47
- DEPENDENCIES_LOADED = False
48
-
49
- @app.get("/")
50
- async def root():
51
- return {"message": "Video Summarizer API", "status": "running"}
52
-
53
- @app.get("/health")
54
- async def health_check():
55
- status = "healthy" if DEPENDENCIES_LOADED else "missing_dependencies"
56
- return {
57
- "status": status,
58
- "service": "python-video-processor",
59
- "dependencies_loaded": DEPENDENCIES_LOADED
60
- }
61
-
62
- @app.post("/process-video")
63
- async def process_video(video: UploadFile = File(...)):
64
- if not DEPENDENCIES_LOADED:
65
- raise HTTPException(
66
- status_code=500,
67
- detail="Required AI dependencies not loaded. Check server logs."
68
- )
69
-
70
- temp_video_path = None
71
- audio_path = "temp_audio.wav"
72
-
73
- try:
74
- # Validate file type
75
- allowed_extensions = {'.mp4', '.avi', '.mov', '.mkv', '.wmv'}
76
- file_extension = os.path.splitext(video.filename)[1].lower()
77
- if file_extension not in allowed_extensions:
78
- raise HTTPException(
79
- status_code=400,
80
- detail=f"Invalid video format. Allowed: {', '.join(allowed_extensions)}"
81
- )
82
-
83
- # Create temporary file
84
- temp_video_path = f"temp_{video.filename}"
85
-
86
- # Save uploaded file
87
- logger.info(f"Saving uploaded file: {video.filename}")
88
- async with aiofiles.open(temp_video_path, 'wb') as out_file:
89
- content = await video.read()
90
- await out_file.write(content)
91
-
92
- start_time = datetime.now()
93
-
94
- # 1. Extract audio
95
- logger.info("Step 1: Extracting audio from video...")
96
- if not os.path.exists(temp_video_path):
97
- raise HTTPException(status_code=500, detail="Video file not found after upload")
98
-
99
- extract_audio(temp_video_path, audio_path)
100
-
101
- if not os.path.exists(audio_path):
102
- raise HTTPException(status_code=500, detail="Audio extraction failed")
103
-
104
- # 2. Transcribe audio
105
- logger.info("Step 2: Transcribing audio...")
106
- transcript = transcribe_audio(audio_path, model_size="base")
107
- logger.info(f"Transcript length: {len(transcript)} characters")
108
-
109
- if not transcript or len(transcript.strip()) < 10:
110
- raise HTTPException(status_code=500, detail="Transcription failed or too short")
111
-
112
- # 3. Summarize text with chunking
113
- logger.info("Step 3: Generating summary...")
114
- final_summary = chunked_summarize(
115
- text=transcript,
116
- summarize_func=lambda text: summarize_text(text, model_name="facebook/bart-large-cnn"),
117
- max_chunk_size=1500
118
- )
119
-
120
- if not final_summary or len(final_summary.strip()) < 10:
121
- raise HTTPException(status_code=500, detail="Summary generation failed")
122
-
123
- processing_time = (datetime.now() - start_time).total_seconds()
124
-
125
- logger.info(f"Processing completed in {processing_time:.2f} seconds")
126
-
127
- return {
128
- "success": True,
129
- "summary": final_summary,
130
- "transcript": transcript,
131
- "processing_time": processing_time
132
- }
133
-
134
- except Exception as e:
135
- logger.error(f"Error processing video: {str(e)}")
136
- logger.error(traceback.format_exc())
137
- raise HTTPException(
138
- status_code=500,
139
- detail=f"Processing failed: {str(e)}"
140
- )
141
-
142
- finally:
143
- # Cleanup temporary files
144
- try:
145
- if temp_video_path and os.path.exists(temp_video_path):
146
- os.remove(temp_video_path)
147
- logger.info(f"Cleaned up: {temp_video_path}")
148
- if os.path.exists(audio_path):
149
- os.remove(audio_path)
150
- logger.info(f"Cleaned up: {audio_path}")
151
- except Exception as cleanup_error:
152
- logger.error(f"Cleanup error: {cleanup_error}")
153
-
154
- @app.post("/recommend-courses")
155
- async def get_course_recommendations(
156
- enrolled_courses: List[Dict[str, Any]],
157
- all_courses: List[Dict[str, Any]],
158
- top_n: int = Query(5, description="Number of recommendations to return")
159
- ):
160
- """
161
- Get course recommendations based on enrolled courses using AI semantic similarity
162
- """
163
- if not DEPENDENCIES_LOADED:
164
- raise HTTPException(
165
- status_code=500,
166
- detail="Required AI dependencies not loaded. Check server logs."
167
- )
168
-
169
- try:
170
- logger.info(f"Generating recommendations for {len(enrolled_courses)} enrolled courses from {len(all_courses)} total courses")
171
-
172
- recommended_ids = recommend_courses(enrolled_courses, all_courses, top_n)
173
-
174
- # Get the recommended course details
175
- recommended_courses = [course for course in all_courses if course['id'] in recommended_ids]
176
-
177
- logger.info(f"Successfully generated {len(recommended_courses)} recommendations")
178
-
179
- return {
180
- "success": True,
181
- "recommendations": recommended_courses,
182
- "count": len(recommended_courses)
183
- }
184
-
185
- except Exception as e:
186
- logger.error(f"Error generating recommendations: {str(e)}")
187
- logger.error(traceback.format_exc())
188
- raise HTTPException(
189
- status_code=500,
190
- detail=f"Recommendation generation failed: {str(e)}"
191
- )
192
-
193
- if __name__ == "__main__":
194
- logger.info("Starting Python Video Summarizer Server...")
195
- logger.info("Dependencies loaded: %s", DEPENDENCIES_LOADED)
196
-
197
- if not DEPENDENCIES_LOADED:
198
- logger.error("CRITICAL: AI dependencies not loaded. Video processing will not work!")
199
- logger.error("Please check that whisper-openai, transformers, and torch are installed.")
200
-
201
- port = int(os.environ.get("PORT", 7860))
202
- uvicorn.run(
203
- "app:app",
204
- host="0.0.0.0",
205
- port=port,
206
- reload=False
207
- )
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, UploadFile, File, HTTPException, Query
2
+ from fastapi.middleware.cors import CORSMiddleware
3
+ import uvicorn
4
+ import os
5
+ import tempfile
6
+ import aiofiles
7
+ from datetime import datetime
8
+ import traceback
9
+ import logging
10
+ from typing import List, Dict, Any
11
+ import httpx
12
+
13
+ # Setup logging
14
+ logging.basicConfig(level=logging.INFO)
15
+ logger = logging.getLogger(__name__)
16
+
17
+ app = FastAPI(title="Video Summarizer API")
18
+
19
+ # Load environment variables
20
+ import os
21
+ from dotenv import load_dotenv
22
+ load_dotenv()
23
+
24
+ # Get URLs from environment
25
+ FRONTEND_URL = os.getenv('FRONTEND_URL')
26
+ BACKEND_URL = os.getenv('BACKEND_URL', 'http://localhost:5000')
27
+
28
+ # CORS middleware
29
+ app.add_middleware(
30
+ CORSMiddleware,
31
+ allow_origins=[FRONTEND_URL, BACKEND_URL],
32
+ allow_credentials=True,
33
+ allow_methods=["*"],
34
+ allow_headers=["*"],
35
+ )
36
+
37
+ # Import processing functions with error handling
38
+ try:
39
+ from transcriber import extract_audio, transcribe_audio
40
+ from summarizer import summarize_text
41
+ from recommendation import recommend_courses
42
+ from utils import chunked_summarize
43
+ DEPENDENCIES_LOADED = True
44
+ logger.info("All AI dependencies loaded successfully")
45
+ except ImportError as e:
46
+ logger.error(f"Import error: {e}")
47
+ DEPENDENCIES_LOADED = False
48
+
49
+ @app.get("/")
50
+ async def root():
51
+ return {"message": "Video Summarizer API", "status": "running"}
52
+
53
+ @app.get("/health")
54
+ async def health_check():
55
+ status = "healthy" if DEPENDENCIES_LOADED else "missing_dependencies"
56
+ return {
57
+ "status": status,
58
+ "service": "python-video-processor",
59
+ "dependencies_loaded": DEPENDENCIES_LOADED
60
+ }
61
+
62
+ @app.post("/process-video")
63
+ async def process_video(video: UploadFile = File(...)):
64
+ if not DEPENDENCIES_LOADED:
65
+ raise HTTPException(
66
+ status_code=500,
67
+ detail="Required AI dependencies not loaded. Check server logs."
68
+ )
69
+
70
+ # Add file size validation for Hugging Face limits
71
+ max_size = 50 * 1024 * 1024 # 50MB limit for Hugging Face
72
+ content = await video.read()
73
+ if len(content) > max_size:
74
+ raise HTTPException(
75
+ status_code=413,
76
+ detail=f"File too large. Max size: 50MB"
77
+ )
78
+ await video.seek(0) # Reset file pointer
79
+
80
+ temp_video_path = None
81
+ audio_path = "temp_audio.wav"
82
+
83
+ try:
84
+ # Validate file type
85
+ allowed_extensions = {'.mp4', '.avi', '.mov', '.mkv', '.wmv'}
86
+ file_extension = os.path.splitext(video.filename)[1].lower()
87
+ if file_extension not in allowed_extensions:
88
+ raise HTTPException(
89
+ status_code=400,
90
+ detail=f"Invalid video format. Allowed: {', '.join(allowed_extensions)}"
91
+ )
92
+
93
+ # Create temporary file
94
+ temp_video_path = f"temp_{video.filename}"
95
+
96
+ # Save uploaded file
97
+ logger.info(f"Saving uploaded file: {video.filename}")
98
+ async with aiofiles.open(temp_video_path, 'wb') as out_file:
99
+ content = await video.read()
100
+ await out_file.write(content)
101
+
102
+ start_time = datetime.now()
103
+
104
+ # 1. Extract audio
105
+ logger.info("Step 1: Extracting audio from video...")
106
+ if not os.path.exists(temp_video_path):
107
+ raise HTTPException(status_code=500, detail="Video file not found after upload")
108
+
109
+ extract_audio(temp_video_path, audio_path)
110
+
111
+ if not os.path.exists(audio_path):
112
+ raise HTTPException(status_code=500, detail="Audio extraction failed")
113
+
114
+ # 2. Transcribe audio
115
+ logger.info("Step 2: Transcribing audio...")
116
+ transcript = transcribe_audio(audio_path, model_size="base")
117
+ logger.info(f"Transcript length: {len(transcript)} characters")
118
+
119
+ if not transcript or len(transcript.strip()) < 10:
120
+ raise HTTPException(status_code=500, detail="Transcription failed or too short")
121
+
122
+ # 3. Summarize text with chunking
123
+ logger.info("Step 3: Generating summary...")
124
+ final_summary = chunked_summarize(
125
+ text=transcript,
126
+ summarize_func=lambda text: summarize_text(text, model_name="facebook/bart-large-cnn"),
127
+ max_chunk_size=1500
128
+ )
129
+
130
+ if not final_summary or len(final_summary.strip()) < 10:
131
+ raise HTTPException(status_code=500, detail="Summary generation failed")
132
+
133
+ processing_time = (datetime.now() - start_time).total_seconds()
134
+
135
+ logger.info(f"Processing completed in {processing_time:.2f} seconds")
136
+
137
+ return {
138
+ "success": True,
139
+ "summary": final_summary,
140
+ "transcript": transcript,
141
+ "processing_time": processing_time
142
+ }
143
+
144
+ except Exception as e:
145
+ logger.error(f"Error processing video: {str(e)}")
146
+ logger.error(traceback.format_exc())
147
+ raise HTTPException(
148
+ status_code=500,
149
+ detail=f"Processing failed: {str(e)}"
150
+ )
151
+
152
+ finally:
153
+ # Cleanup temporary files
154
+ try:
155
+ if temp_video_path and os.path.exists(temp_video_path):
156
+ os.remove(temp_video_path)
157
+ logger.info(f"Cleaned up: {temp_video_path}")
158
+ if os.path.exists(audio_path):
159
+ os.remove(audio_path)
160
+ logger.info(f"Cleaned up: {audio_path}")
161
+ except Exception as cleanup_error:
162
+ logger.error(f"Cleanup error: {cleanup_error}")
163
+
164
+ @app.post("/recommend-courses")
165
+ async def get_course_recommendations(
166
+ enrolled_courses: List[Dict[str, Any]],
167
+ all_courses: List[Dict[str, Any]],
168
+ top_n: int = Query(5, description="Number of recommendations to return")
169
+ ):
170
+ """
171
+ Get course recommendations based on enrolled courses using AI semantic similarity
172
+ """
173
+ if not DEPENDENCIES_LOADED:
174
+ raise HTTPException(
175
+ status_code=500,
176
+ detail="Required AI dependencies not loaded. Check server logs."
177
+ )
178
+
179
+ try:
180
+ logger.info(f"Generating recommendations for {len(enrolled_courses)} enrolled courses from {len(all_courses)} total courses")
181
+
182
+ recommended_ids = recommend_courses(enrolled_courses, all_courses, top_n)
183
+
184
+ # Get the recommended course details
185
+ recommended_courses = [course for course in all_courses if course['id'] in recommended_ids]
186
+
187
+ logger.info(f"Successfully generated {len(recommended_courses)} recommendations")
188
+
189
+ return {
190
+ "success": True,
191
+ "recommendations": recommended_courses,
192
+ "count": len(recommended_courses)
193
+ }
194
+
195
+ except Exception as e:
196
+ logger.error(f"Error generating recommendations: {str(e)}")
197
+ logger.error(traceback.format_exc())
198
+ raise HTTPException(
199
+ status_code=500,
200
+ detail=f"Recommendation generation failed: {str(e)}"
201
+ )
202
+
203
+ if __name__ == "__main__":
204
+ logger.info("Starting Python Video Summarizer Server...")
205
+ logger.info("Dependencies loaded: %s", DEPENDENCIES_LOADED)
206
+
207
+ if not DEPENDENCIES_LOADED:
208
+ logger.error("CRITICAL: AI dependencies not loaded. Video processing will not work!")
209
+ logger.error("Please check that whisper-openai, transformers, and torch are installed.")
210
+
211
+ port = int(os.environ.get("PORT", 7860))
212
+ uvicorn.run(
213
+ "app:app",
214
+ host="0.0.0.0",
215
+ port=port,
216
+ reload=False
217
+ )