Spaces:
Sleeping
Sleeping
| import os | |
| import sys | |
| import time | |
| import tempfile | |
| import uuid | |
| import torch | |
| import numpy as np | |
| from flask import Blueprint, request, jsonify, current_app | |
| sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) | |
| from config import config | |
| from utils.video_utils import load_video_frames, normalize_frames, validate_video | |
| predict_bp = Blueprint("predict", __name__) | |
| ALLOWED_EXTENSIONS = {".mp4", ".avi", ".mkv", ".mov", ".webm"} | |
| def _allowed_file(filename: str) -> bool: | |
| return os.path.splitext(filename.lower())[1] in ALLOWED_EXTENSIONS | |
| def predict(): | |
| # ββ Validate upload βββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| if "video" not in request.files: | |
| return jsonify({"error": "No video file provided"}), 400 | |
| file = request.files["video"] | |
| if file.filename == "": | |
| return jsonify({"error": "Empty filename"}), 400 | |
| if not _allowed_file(file.filename): | |
| exts = ", ".join(ALLOWED_EXTENSIONS) | |
| return jsonify({"error": f"Unsupported format. Allowed: {exts}"}), 415 | |
| # ββ Save to temp path βββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| suffix = os.path.splitext(file.filename)[1] | |
| tmp_path = os.path.join(tempfile.gettempdir(), f"aivd_{uuid.uuid4().hex}{suffix}") | |
| try: | |
| file.save(tmp_path) | |
| except Exception as e: | |
| return jsonify({"error": f"Could not save file: {e}"}), 500 | |
| # ββ Inference βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| t_start = time.time() | |
| try: | |
| model = current_app.model | |
| device = current_app.device | |
| if not validate_video(tmp_path): | |
| return jsonify({"error": "Invalid or corrupted video file"}), 422 | |
| frames = load_video_frames( | |
| tmp_path, | |
| num_frames=config.FRAMES_PER_VIDEO, | |
| frame_size=(config.FRAME_HEIGHT, config.FRAME_WIDTH), | |
| frame_skip=config.FRAME_SKIP, | |
| ) | |
| if frames is None: | |
| return jsonify({"error": "Could not extract frames from video"}), 422 | |
| frames = normalize_frames(frames) | |
| frames_tensor = torch.from_numpy(frames).float() | |
| frames_tensor = frames_tensor.permute(0, 3, 1, 2) # (T, 3, H, W) | |
| frames_tensor = frames_tensor.unsqueeze(0) # (1, T, 3, H, W) | |
| frames_tensor = frames_tensor.to(device) | |
| with torch.no_grad(): | |
| output = model(frames_tensor) | |
| prob = torch.sigmoid(output).item() | |
| threshold = config.PREDICTION_THRESHOLD | |
| is_ai = prob > threshold | |
| verdict = "AI GENERATED" if is_ai else "REAL VIDEO" | |
| confidence = prob if is_ai else (1.0 - prob) | |
| elapsed = round(time.time() - t_start, 2) | |
| return jsonify({ | |
| "verdict": verdict, | |
| "is_ai": is_ai, | |
| "probability": round(prob, 6), | |
| "confidence": round(confidence * 100, 2), | |
| "processing_time": elapsed, | |
| "threshold": threshold, | |
| }) | |
| except Exception as e: | |
| return jsonify({"error": str(e)}), 500 | |
| finally: | |
| try: | |
| os.remove(tmp_path) | |
| except OSError: | |
| pass | |