File size: 4,899 Bytes
88a0fe8
 
 
 
5f2611d
 
 
 
 
88a0fe8
 
 
 
 
 
5f2611d
 
5cf25d2
5f2611d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88a0fe8
b8e6ffe
88a0fe8
 
 
 
 
 
 
 
 
 
 
 
b8e6ffe
88a0fe8
 
 
 
 
 
 
 
 
b8e6ffe
88a0fe8
 
 
b8e6ffe
88a0fe8
 
 
b8e6ffe
88a0fe8
b8e6ffe
88a0fe8
 
 
 
 
 
 
 
b8e6ffe
 
88a0fe8
 
 
 
 
b8e6ffe
88a0fe8
 
 
 
 
 
 
 
 
 
 
b8e6ffe
 
 
 
 
 
 
 
 
 
88a0fe8
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
import gradio as gr
import requests
import json
import os
import time
from collections import defaultdict
from PIL import Image
import io


BASE_URL = "https://api.jigsawstack.com/v1"
headers = {
    "x-api-key": os.getenv("JIGSAWSTACK_API_KEY")
}

# Rate limiting configuration
request_times = defaultdict(list)
MAX_REQUESTS = 20  # Maximum requests per time window
TIME_WINDOW = 3600   # Time window in seconds (1 hour)

def get_real_ip(request: gr.Request):
    """Extract real IP address using x-forwarded-for header or fallback"""
    if not request:
        return "unknown"
    
    forwarded = request.headers.get("x-forwarded-for")
    if forwarded:
        ip = forwarded.split(",")[0].strip()  # First IP in the list is the client's
    else:
        ip = request.client.host  # fallback
    return ip

def check_rate_limit(request: gr.Request):
    """Check if the current request exceeds rate limits"""
    if not request:
        return True, "Rate limit check failed - no request info"
    
    ip = get_real_ip(request)
    now = time.time()

    # Clean up old timestamps outside the time window
    request_times[ip] = [t for t in request_times[ip] if now - t < TIME_WINDOW]
    
    # Check if rate limit exceeded
    if len(request_times[ip]) >= MAX_REQUESTS:
        time_remaining = int(TIME_WINDOW - (now - request_times[ip][0]))
        time_remaining_minutes = round(time_remaining / 60, 1)      
        time_window_minutes = round(TIME_WINDOW / 60, 1)
        
        return False, f"Rate limit exceeded. You can make {MAX_REQUESTS} requests per {time_window_minutes} minutes. Try again in {time_remaining_minutes} minutes."
    
    # Add current request timestamp
    request_times[ip].append(now)
    return True, ""

def validate_nsfw(url, request: gr.Request):
    # Check rate limit first
    rate_limit_ok, rate_limit_msg = check_rate_limit(request)
    if not rate_limit_ok:
        return None, rate_limit_msg, None, None
    
    if not url or not url.strip():
        return None, "Error: Image URL is required.", None, None

    try:
        response = requests.post(
            f"{BASE_URL}/validate/nsfw",
            headers=headers,
            json={"url": url.strip()}
        )
        response.raise_for_status()
        result = response.json()

        if not result.get("success"):
            error_msg = f"Error: API call failed - {result.get('message', 'Unknown error')}"
            return url, error_msg, None, None

        # Extract detailed information
        nsfw = result.get("nsfw", False)
        nudity = result.get("nudity", False)
        gore = result.get("gore", False)
        nsfw_score = result.get("nsfw_score", 0)
        nudity_score = result.get("nudity_score", 0)
        gore_score = result.get("gore_score", 0)

        # Format the output
        status_message = f"Overall NSFW: {'Yes' if nsfw else 'No'}"
        nudity_details = f"Nudity: {'Detected' if nudity else 'Not Detected'} (Score: {nudity_score:.4f})"
        gore_details = f"Gore: {'Detected' if gore else 'Not Detected'} (Score: {gore_score:.4f})"

        return url, status_message, nudity_details, gore_details

    except requests.exceptions.RequestException as e:
        return url, f"Request failed: {str(e)}", None, None
    except Exception as e:
        return url, f"An unexpected error occurred: {str(e)}", None, None

with gr.Blocks() as demo:
    gr.Markdown("""
        <div style='text-align: center; margin-bottom: 24px;'>
            <h1 style='font-size:2.2em; margin-bottom: 0.2em;'>🧩 NSFW Detection</h1>
            <p style='font-size:1.2em; margin-top: 0;'>Quickly detect nudity, violence, and other NSFW content in images.</p>
            <p style='font-size:1em; margin-top: 0.5em;'>For more details and API usage, see the <a href='https://jigsawstack.com/docs/api-reference/validate/nsfw' target='_blank'>documentation</a>.</p>
        </div>
    """)

    with gr.Row():
        with gr.Column():
            gr.Markdown("#### Image to Analyze")
            nsfw_url = gr.Textbox(
                label="Image URL",
                placeholder="Enter the URL of the image you want to validate..."
            )
            nsfw_btn = gr.Button("Validate Image", variant="primary")

        with gr.Column():
            gr.Markdown("#### Validation Result")
            nsfw_image_display = gr.Image(label="Image Preview")
            nsfw_status_message = gr.Textbox(label="Status", interactive=False)
            nsfw_nudity_details = gr.Textbox(label="Nudity Details", interactive=False)
            nsfw_gore_details = gr.Textbox(label="Gore Details", interactive=False)

    nsfw_btn.click(
        validate_nsfw,
        inputs=[nsfw_url],
        outputs=[
            nsfw_image_display,
            nsfw_status_message,
            nsfw_nudity_details,
            nsfw_gore_details
        ]
    )

demo.launch()