File size: 7,315 Bytes
19f34bb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
from fastapi import FastAPI, UploadFile, File, HTTPException, Query
from fastapi.middleware.cors import CORSMiddleware
import uvicorn
import os
import tempfile
import aiofiles
from datetime import datetime
import traceback
import logging
from typing import List, Dict, Any
import httpx

# Setup logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

app = FastAPI(title="Video Summarizer API")

# Load environment variables
import os
from dotenv import load_dotenv
load_dotenv()

# Get URLs from environment
FRONTEND_URL = os.getenv('FRONTEND_URL')
BACKEND_URL = os.getenv('BACKEND_URL', 'http://localhost:5000')

# CORS middleware
app.add_middleware(
    CORSMiddleware,
    allow_origins=[FRONTEND_URL, BACKEND_URL],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# Import processing functions with error handling
try:
    from transcriber import extract_audio, transcribe_audio
    from summarizer import summarize_text
    from recommendation import recommend_courses
    from utils import chunked_summarize
    DEPENDENCIES_LOADED = True
    logger.info("All AI dependencies loaded successfully")
except ImportError as e:
    logger.error(f"Import error: {e}")
    DEPENDENCIES_LOADED = False

@app.get("/")
async def root():
    return {"message": "Video Summarizer API", "status": "running"}

@app.get("/health")
async def health_check():
    status = "healthy" if DEPENDENCIES_LOADED else "missing_dependencies"
    return {
        "status": status,
        "service": "python-video-processor",
        "dependencies_loaded": DEPENDENCIES_LOADED
    }

@app.post("/process-video")
async def process_video(video: UploadFile = File(...)):
    if not DEPENDENCIES_LOADED:
        raise HTTPException(
            status_code=500,
            detail="Required AI dependencies not loaded. Check server logs."
        )

          # Add file size validation for Hugging Face limits
    max_size = 50 * 1024 * 1024  # 50MB limit for Hugging Face
    content = await video.read()
    if len(content) > max_size:
        raise HTTPException(
            status_code=413, 
            detail=f"File too large. Max size: 50MB"
        )
    await video.seek(0)  # Reset file pointer

    temp_video_path = None
    audio_path = "temp_audio.wav"

    try:
        # Validate file type
        allowed_extensions = {'.mp4', '.avi', '.mov', '.mkv', '.wmv'}
        file_extension = os.path.splitext(video.filename)[1].lower()
        if file_extension not in allowed_extensions:
            raise HTTPException(
                status_code=400,
                detail=f"Invalid video format. Allowed: {', '.join(allowed_extensions)}"
            )

        # Create temporary file
        temp_video_path = f"temp_{video.filename}"

        # Save uploaded file
        logger.info(f"Saving uploaded file: {video.filename}")
        async with aiofiles.open(temp_video_path, 'wb') as out_file:
            content = await video.read()
            await out_file.write(content)

        start_time = datetime.now()

        # 1. Extract audio
        logger.info("Step 1: Extracting audio from video...")
        if not os.path.exists(temp_video_path):
            raise HTTPException(status_code=500, detail="Video file not found after upload")

        extract_audio(temp_video_path, audio_path)

        if not os.path.exists(audio_path):
            raise HTTPException(status_code=500, detail="Audio extraction failed")

        # 2. Transcribe audio
        logger.info("Step 2: Transcribing audio...")
        transcript = transcribe_audio(audio_path, model_size="base")
        logger.info(f"Transcript length: {len(transcript)} characters")

        if not transcript or len(transcript.strip()) < 10:
            raise HTTPException(status_code=500, detail="Transcription failed or too short")

        # 3. Summarize text with chunking
        logger.info("Step 3: Generating summary...")
        final_summary = chunked_summarize(
            text=transcript,
            summarize_func=lambda text: summarize_text(text, model_name="facebook/bart-large-cnn"),
            max_chunk_size=1500
        )

        if not final_summary or len(final_summary.strip()) < 10:
            raise HTTPException(status_code=500, detail="Summary generation failed")

        processing_time = (datetime.now() - start_time).total_seconds()

        logger.info(f"Processing completed in {processing_time:.2f} seconds")

        return {
            "success": True,
            "summary": final_summary,
            "transcript": transcript,
            "processing_time": processing_time
        }

    except Exception as e:
        logger.error(f"Error processing video: {str(e)}")
        logger.error(traceback.format_exc())
        raise HTTPException(
            status_code=500,
            detail=f"Processing failed: {str(e)}"
        )

    finally:
        # Cleanup temporary files
        try:
            if temp_video_path and os.path.exists(temp_video_path):
                os.remove(temp_video_path)
                logger.info(f"Cleaned up: {temp_video_path}")
            if os.path.exists(audio_path):
                os.remove(audio_path)
                logger.info(f"Cleaned up: {audio_path}")
        except Exception as cleanup_error:
            logger.error(f"Cleanup error: {cleanup_error}")

@app.post("/recommend-courses")
async def get_course_recommendations(
    enrolled_courses: List[Dict[str, Any]],
    all_courses: List[Dict[str, Any]],
    top_n: int = Query(5, description="Number of recommendations to return")
):
    """
    Get course recommendations based on enrolled courses using AI semantic similarity
    """
    if not DEPENDENCIES_LOADED:
        raise HTTPException(
            status_code=500,
            detail="Required AI dependencies not loaded. Check server logs."
        )

    try:
        logger.info(f"Generating recommendations for {len(enrolled_courses)} enrolled courses from {len(all_courses)} total courses")

        recommended_ids = recommend_courses(enrolled_courses, all_courses, top_n)

        # Get the recommended course details
        recommended_courses = [course for course in all_courses if course['id'] in recommended_ids]

        logger.info(f"Successfully generated {len(recommended_courses)} recommendations")

        return {
            "success": True,
            "recommendations": recommended_courses,
            "count": len(recommended_courses)
        }

    except Exception as e:
        logger.error(f"Error generating recommendations: {str(e)}")
        logger.error(traceback.format_exc())
        raise HTTPException(
            status_code=500,
            detail=f"Recommendation generation failed: {str(e)}"
        )

if __name__ == "__main__":
    logger.info("Starting Python Video Summarizer Server...")
    logger.info("Dependencies loaded: %s", DEPENDENCIES_LOADED)

    if not DEPENDENCIES_LOADED:
        logger.error("CRITICAL: AI dependencies not loaded. Video processing will not work!")
        logger.error("Please check that whisper-openai, transformers, and torch are installed.")

    port = int(os.environ.get("PORT", 7860))
    uvicorn.run(
        "app:app",
        host="0.0.0.0",
        port=port,
        reload=False
    )