Spaces:
Sleeping
Sleeping
bahaeddinmselmi
fix(analyzer): robust doubled-URL normalization and enhanced error reporting
c9f75f9 | # C:\Users\bahae\.gemini\antigravity\scratch\verivid-ai\backend\app\services\sightengine.py | |
| import requests | |
| from app.core.config import settings | |
| SIGHTENGINE_CHECK_URL = "https://api.sightengine.com/1.0/check.json" | |
| def analyze_with_sightengine(image_url: str = None, image_bytes: bytes = None) -> dict: | |
| """ | |
| Use SightEngine's professional AI detection. | |
| Returns: {"ai_score": 0-1, "details": str, "raw": dict} | |
| """ | |
| if not settings.SIGHTENGINE_USER or not settings.SIGHTENGINE_SECRET: | |
| return {"ai_score": None, "details": "SightEngine not configured", "raw": None} | |
| try: | |
| if image_url: | |
| # URL-based check | |
| response = requests.post( | |
| SIGHTENGINE_CHECK_URL, | |
| data={ | |
| "url": image_url, | |
| "models": "genai", | |
| "api_user": settings.SIGHTENGINE_USER, | |
| "api_secret": settings.SIGHTENGINE_SECRET | |
| }, | |
| timeout=30 | |
| ) | |
| elif image_bytes: | |
| # File-based check | |
| response = requests.post( | |
| SIGHTENGINE_CHECK_URL, | |
| data={ | |
| "models": "genai", | |
| "api_user": settings.SIGHTENGINE_USER, | |
| "api_secret": settings.SIGHTENGINE_SECRET | |
| }, | |
| files={"media": ("image.jpg", image_bytes, "image/jpeg")}, | |
| timeout=30 | |
| ) | |
| else: | |
| return {"ai_score": None, "details": "No image provided", "raw": None} | |
| if response.status_code != 200: | |
| return {"ai_score": None, "details": f"API error: {response.status_code}", "raw": response.text[:200]} | |
| data = response.json() | |
| # SightEngine returns: {"type": {"ai_generated": 0.95, ...}} | |
| if data.get("status") == "success": | |
| genai_data = data.get("type", {}) | |
| ai_score = genai_data.get("ai_generated", 0) | |
| return { | |
| "ai_score": ai_score, | |
| "details": f"SightEngine AI detection: {round(ai_score * 100)}% AI probability", | |
| "raw": data | |
| } | |
| else: | |
| return {"ai_score": None, "details": f"API error: {data.get('error', {}).get('message', 'Unknown')}", "raw": data} | |
| except Exception as e: | |
| return {"ai_score": None, "details": f"Exception: {str(e)}", "raw": None} | |
| def analyze_frames_with_sightengine(frame_paths: list) -> dict: | |
| """Analyze multiple frames and aggregate scores""" | |
| scores = [] | |
| details = [] | |
| for path in frame_paths[:5]: # Limit to 5 frames to save API calls | |
| try: | |
| with open(path, 'rb') as f: | |
| img_bytes = f.read() | |
| result = analyze_with_sightengine(image_bytes=img_bytes) | |
| if result["ai_score"] is not None: | |
| scores.append(result["ai_score"]) | |
| details.append(f"Frame: {round(result['ai_score'] * 100)}%") | |
| except Exception as e: | |
| details.append(f"Error: {str(e)[:50]}") | |
| if scores: | |
| avg_score = sum(scores) / len(scores) | |
| max_score = max(scores) | |
| return { | |
| "avg_score": avg_score, | |
| "max_score": max_score, | |
| "frame_count": len(scores), | |
| "frame_scores": [round(s, 3) for s in scores], | |
| "details": f"SightEngine analyzed {len(scores)} frames. Avg: {round(avg_score*100)}%, Max: {round(max_score*100)}%" | |
| } | |
| else: | |
| return { | |
| "avg_score": None, | |
| "max_score": None, | |
| "frame_count": 0, | |
| "frame_scores": [], | |
| "details": "SightEngine analysis failed: " + "; ".join(details) | |
| } | |