from fastapi import FastAPI, HTTPException, UploadFile, File, Form, BackgroundTasks, Depends, Request from fastapi.staticfiles import StaticFiles from fastapi.responses import HTMLResponse, JSONResponse from fastapi.templating import Jinja2Templates # For serving HTML from pydantic import BaseModel, Field from typing import List, Dict, Optional, Union import cv2 # OpenCV for video processing import uuid # For generating unique filenames import os # For interacting with the file system import requests # For making HTTP requests import random import string import json import shutil # For file operations import ast # For safely evaluating string literals import tempfile # For creating temporary directories/files import asyncio # For concurrent operations import time # For retries and delays import logging # For structured logging # --- Application Setup --- app = FastAPI(title="Advanced NSFW Video Detector API", version="1.1.0") # Updated version # --- Logging Configuration --- logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') logger = logging.getLogger(__name__) # --- Templates for HTML Homepage --- # Create a 'templates' directory in the same location as your main.py # and put an 'index.html' file inside it. # For Hugging Face Spaces, you might need to adjust path or ensure the templates dir is included. # For simplicity here, I'll embed the HTML string directly if Jinja2 setup is complex for the environment. # However, using Jinja2 is cleaner. Let's assume a 'templates' directory. # If 'templates' dir doesn't exist, it will fall back to a basic HTML string. try: templates_path = os.path.join(os.path.dirname(__file__), "templates") if not os.path.exists(templates_path): os.makedirs(templates_path) # Create if not exists for local dev templates = Jinja2Templates(directory=templates_path) # Create a dummy index.html if it doesn't exist for local testing dummy_html_path = os.path.join(templates_path, "index.html") if not os.path.exists(dummy_html_path): with open(dummy_html_path, "w") as f: f.write("
This API allows you to process videos to detect Not Suitable For Work (NSFW) content. It works asynchronously: you submit a video (via URL or direct upload), receive a task ID, and then poll a status endpoint to get the results.
Important: The app_base_url
parameter is crucial. It must be the public base URL where this API service is accessible. For example, if your Hugging Face Space URL is https://your-username-your-space-name.hf.space
, then that's your app_base_url
.
Current detected example base URL for instructions: {example_app_base_url}
(This is a guess, please verify your actual public URL).
POST /process_video_async
Submits a video from a public URL for NSFW analysis.
Parameter | Type | Default | Description |
---|---|---|---|
video_url | string | Required | Publicly accessible URL of the video. |
num_frames | integer | 10 | Number of frames to extract and analyze (1-50). |
app_base_url | string | Required | The public base URL of this API service. |
curl
:curl -X POST "{example_app_base_url}/process_video_async" \\
-H "Content-Type: application/json" \\
-d '{{
"video_url": "YOUR_PUBLIC_VIDEO_URL_HERE.mp4",
"num_frames": 5,
"app_base_url": "{example_app_base_url}"
}}'
POST /upload_video_async
Uploads a video file directly for NSFW analysis.
Parameter | Type | Default | Description |
---|---|---|---|
video_file | file | Required | The video file to upload. |
num_frames | integer | 10 | Number of frames to extract (1-50). |
app_base_url | string | Required | The public base URL of this API service. |
curl
:curl -X POST "{example_app_base_url}/upload_video_async" \\
-F "video_file=@/path/to/your/video.mp4" \\
-F "num_frames=5" \\
-F "app_base_url={example_app_base_url}"
If successful (HTTP 202 Accepted), the API will respond with:
{{
"task_id": "some-unique-task-id",
"status_url": "{example_app_base_url}/tasks/some-unique-task-id/status",
"message": "Video processing task accepted and started in background."
}}
GET /tasks/<task_id>/status
Poll this endpoint to check the status of a processing task and retrieve the result once completed.
curl
:curl -X GET "{example_app_base_url}/tasks/some-unique-task-id/status"
pending
: Task is queued.processing
: Task is actively being processed (downloading, extracting frames, analyzing).completed
: Task finished successfully. Results are available in the result
field.failed
: Task failed. Check the message
field for details.completed
):{{
"task_id": "some-unique-task-id",
"status": "completed",
"message": "Processing complete.",
"result": {{
"nsfw_count": 1,
"total_frames_analyzed": 5,
"frames": [
{{
"frame_url": "{example_app_base_url}/static_frames/some-unique-task-id/frame_uuid1.jpg",
"nsfw_detected": "false"
}},
{{
"frame_url": "{example_app_base_url}/static_frames/some-unique-task-id/frame_uuid2.jpg",
"nsfw_detected": "true"
}}
// ... more frames
]
}}
}}
processing
):{{
"task_id": "some-unique-task-id",
"status": "processing",
"message": "Analyzing 5 frames...",
"result": null
}}
API Version: {app.version}