Rthur2003 commited on
Commit
5b137e3
·
1 Parent(s): 3203a8c

Add unified analysis endpoint for YouTube URLs and file uploads

Browse files
Files changed (1) hide show
  1. app/routes/analyze.py +144 -0
app/routes/analyze.py ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ General analysis route for CrownCode.
3
+ Handles YouTube URLs and file uploads through a unified endpoint.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ import uuid
9
+ from typing import Optional, List, Dict, Any
10
+ from fastapi import APIRouter, File, UploadFile, Form, HTTPException
11
+ from pydantic import BaseModel
12
+
13
+ from ..services.youtube_analysis import YouTubeAnalysisService
14
+ from ..services.logging_config import get_logger
15
+
16
+ router = APIRouter()
17
+ logger = get_logger(__name__)
18
+ youtube_service = YouTubeAnalysisService()
19
+
20
+
21
+ class AnalysisResult(BaseModel):
22
+ """Unified analysis result structure."""
23
+ request_id: str
24
+ source_type: str
25
+ is_ai_generated: bool
26
+ confidence: float
27
+ model_version: str
28
+ indicators: List[str]
29
+ metadata: Dict[str, Any]
30
+
31
+
32
+ class AnalyzeResponse(BaseModel):
33
+ """Response structure matching frontend expectations."""
34
+ result: Optional[AnalysisResult] = None
35
+ warnings: List[str] = []
36
+ errors: List[str] = []
37
+
38
+
39
+ @router.post("/api/analyze", response_model=AnalyzeResponse)
40
+ async def analyze(
41
+ sourceType: str = Form(...),
42
+ url: Optional[str] = Form(None),
43
+ file: Optional[UploadFile] = File(None)
44
+ ) -> AnalyzeResponse:
45
+ """
46
+ Unified analysis endpoint.
47
+
48
+ Args:
49
+ sourceType: Type of source (youtube, file, spotify, apple)
50
+ url: URL for YouTube/Spotify/Apple Music
51
+ file: Uploaded audio file
52
+
53
+ Returns:
54
+ AnalyzeResponse with result or errors
55
+ """
56
+ request_id = str(uuid.uuid4())[:8]
57
+ logger.info(f"[{request_id}] Analysis request: sourceType={sourceType}")
58
+
59
+ warnings: List[str] = []
60
+ errors: List[str] = []
61
+
62
+ try:
63
+ if sourceType == "youtube":
64
+ if not url:
65
+ return AnalyzeResponse(errors=["missing_url"])
66
+
67
+ # Use YouTube analysis service
68
+ try:
69
+ yt_result = await youtube_service.analyze(url, include_raw=False)
70
+
71
+ result = AnalysisResult(
72
+ request_id=request_id,
73
+ source_type="youtube",
74
+ is_ai_generated=yt_result.summary.is_ai_generated,
75
+ confidence=yt_result.summary.confidence,
76
+ model_version=yt_result.summary.model_version,
77
+ indicators=yt_result.summary.indicators,
78
+ metadata={
79
+ "video_id": yt_result.source.video_id,
80
+ "title": yt_result.source.title,
81
+ "duration_sec": yt_result.source.duration_sec,
82
+ "decision_source": yt_result.summary.decision_source
83
+ }
84
+ )
85
+
86
+ return AnalyzeResponse(
87
+ result=result,
88
+ warnings=yt_result.warnings,
89
+ errors=yt_result.errors
90
+ )
91
+
92
+ except ValueError as e:
93
+ logger.warning(f"[{request_id}] YouTube analysis error: {e}")
94
+ return AnalyzeResponse(errors=["invalid_youtube_url"])
95
+ except Exception as e:
96
+ logger.error(f"[{request_id}] YouTube analysis failed: {e}")
97
+ return AnalyzeResponse(errors=["youtube_analysis_failed"])
98
+
99
+ elif sourceType == "file":
100
+ if not file:
101
+ return AnalyzeResponse(errors=["missing_file"])
102
+
103
+ # Validate file type
104
+ if not file.content_type or not file.content_type.startswith("audio/"):
105
+ return AnalyzeResponse(errors=["invalid_file_type"])
106
+
107
+ # Read file content
108
+ content = await file.read()
109
+ file_size_mb = len(content) / (1024 * 1024)
110
+
111
+ if file_size_mb > 30:
112
+ return AnalyzeResponse(errors=["file_too_large"])
113
+
114
+ logger.info(f"[{request_id}] Processing file: {file.filename} ({file_size_mb:.2f}MB)")
115
+
116
+ # TODO: Implement actual audio analysis
117
+ # For now, return a placeholder result
118
+ warnings.append("file_analysis_not_implemented")
119
+
120
+ result = AnalysisResult(
121
+ request_id=request_id,
122
+ source_type="file",
123
+ is_ai_generated=False,
124
+ confidence=0.0,
125
+ model_version="placeholder",
126
+ indicators=["file_analysis_pending"],
127
+ metadata={
128
+ "filename": file.filename,
129
+ "size_mb": round(file_size_mb, 2),
130
+ "content_type": file.content_type
131
+ }
132
+ )
133
+
134
+ return AnalyzeResponse(result=result, warnings=warnings)
135
+
136
+ elif sourceType in ["spotify", "apple"]:
137
+ return AnalyzeResponse(errors=["unsupported_source"])
138
+
139
+ else:
140
+ return AnalyzeResponse(errors=["invalid_source_type"])
141
+
142
+ except Exception as e:
143
+ logger.error(f"[{request_id}] Unexpected error: {e}", exc_info=True)
144
+ return AnalyzeResponse(errors=["internal_error"])