Spaces:
Sleeping
Sleeping
| import os | |
| import time | |
| import requests | |
| import gradio as gr | |
| HTML_HEADER = """ | |
| <header style="text-align: center; padding: 20px; border-bottom: 2px solid #cc3300;"> | |
| <h1>Demo of Audio Deepfake Detection</h1> | |
| <p style="font-size: 18px;"> | |
| To learn more, visit our website: <a href="https://dataspike.io/" target="_blank" style="font-size: 20px; text-decoration: none;"> | |
| https://dataspike.io/ </a> | |
| </p> | |
| <img src="https://api.visitorbadge.io/api/visitors?path=https%3A%2F%2Fhuggingface.co%2Fspaces%2Fdataspike%2FAudio-Deepfake-Detection&countColor=%23263759" | |
| alt="Visitor Count" | |
| style="display: none;"> | |
| </header> | |
| """ | |
| def get_verdict_html(score, verdict): | |
| """Generate HTML verdict block based on score and verdict.""" | |
| # Determine color and label based on verdict | |
| if verdict == "deepfake_likely": | |
| color = "#E74C3C" | |
| label = "Deepfake" | |
| description = "This audio shows strong signs of manipulation (likely a deepfake)." | |
| elif verdict == "deepfake_unlikely": | |
| color = "#2ECC71" | |
| label = "Not Deepfake" | |
| description = "This audio appears to be genuine." | |
| else: | |
| color = "#F39C12" | |
| label = "Uncertain" | |
| description = "The analysis is inconclusive. Further verification may be needed." | |
| # Convert score to percentage | |
| percentage = int(score * 100) | |
| html = f""" | |
| <div style=" | |
| background: linear-gradient(135deg, {color}15 0%, {color}05 100%); | |
| border: 2px solid {color}; | |
| border-radius: 12px; | |
| padding: 24px; | |
| margin: 20px 0; | |
| text-align: center; | |
| "> | |
| <h2 style="color: {color}; margin: 0 0 12px 0; font-size: 32px;">{label}</h2> | |
| <p style="font-size: 18px; color: #E0E0E0; margin: 12px 0;">{description}</p> | |
| <div style=" | |
| background: rgba(0,0,0,0.3); | |
| border-radius: 8px; | |
| padding: 16px; | |
| margin-top: 16px; | |
| "> | |
| <p style="font-size: 16px; color: #B0B0B0; margin: 0;">Deepfake Probability</p> | |
| <p style="font-size: 36px; font-weight: bold; color: {color}; margin: 8px 0 0 0;">{percentage}%</p> | |
| </div> | |
| </div> | |
| """ | |
| return html | |
| def api_status(job_id, poll_interval=1.0, max_attempts=120): | |
| """Poll API for job status until completion.""" | |
| url = f"https://api.dataspike.io/api/v4/deepfake/job/{job_id}" | |
| headers = {"ds-api-token": os.getenv("API_KEY")} | |
| for attempt in range(max_attempts): | |
| try: | |
| resp = requests.get(url, headers=headers, timeout=10) | |
| result = resp.json() | |
| status = result.get("status", "") | |
| if status in ("completed", "done", "error", "failed"): | |
| return result | |
| time.sleep(poll_interval) | |
| except Exception as e: | |
| # If polling fails, return error | |
| return { | |
| "status": "error", | |
| "errors": [f"Polling failed: {str(e)}"] | |
| } | |
| # If max attempts reached | |
| return { | |
| "status": "error", | |
| "errors": ["Timeout: processing took too long"] | |
| } | |
| def check_audio_deepfake(file_path): | |
| """Check if audio is a deepfake using the API.""" | |
| if not file_path: | |
| return """ | |
| <div style=" | |
| background: rgba(231, 76, 60, 0.1); | |
| border: 2px solid #E74C3C; | |
| border-radius: 12px; | |
| padding: 24px; | |
| margin: 20px 0; | |
| text-align: center; | |
| "> | |
| <h2 style="color: #E74C3C; margin: 0 0 12px 0;">No Audio File</h2> | |
| <p style="font-size: 18px; color: #E0E0E0;">Please submit an audio file first and then click the button 'Check Audio!'</p> | |
| </div> | |
| """ | |
| url = "https://api.dataspike.io/api/v4/deepfake/audio/analyze" | |
| headers = {"ds-api-token": os.getenv("API_KEY")} | |
| try: | |
| # Step 1: Submit audio file and get job_id | |
| with open(file_path, "rb") as f: | |
| files = {"file": f} | |
| response = requests.post(url, headers=headers, files=files) | |
| job_response = response.json() | |
| # Check if we got a job_id | |
| if "id" not in job_response: | |
| return """ | |
| <div style=" | |
| background: rgba(231, 76, 60, 0.1); | |
| border: 2px solid #E74C3C; | |
| border-radius: 12px; | |
| padding: 24px; | |
| margin: 20px 0; | |
| text-align: center; | |
| "> | |
| <h2 style="color: #E74C3C; margin: 0 0 12px 0;">Upload Failed</h2> | |
| <p style="font-size: 18px; color: #E0E0E0;">Failed to upload audio file. Please try again.</p> | |
| </div> | |
| """ | |
| job_id = job_response["id"] | |
| # Step 2: Poll for results | |
| deepfake_result = api_status(job_id) | |
| # Check verdict and show appropriate message | |
| if deepfake_result.get("status") in ("completed", "done"): | |
| score = deepfake_result.get("score", 0) | |
| verdict = deepfake_result.get("verdict", "unknown") | |
| return get_verdict_html(score, verdict) | |
| elif deepfake_result.get("status") in ("error", "failed"): | |
| return """ | |
| <div style=" | |
| background: rgba(231, 76, 60, 0.1); | |
| border: 2px solid #E74C3C; | |
| border-radius: 12px; | |
| padding: 24px; | |
| margin: 20px 0; | |
| text-align: center; | |
| "> | |
| <h2 style="color: #E74C3C; margin: 0 0 12px 0;">Detection Failed</h2> | |
| <p style="font-size: 18px; color: #E0E0E0;">The audio quality is not acceptable or the file is corrupted. Please try with a different audio file.</p> | |
| </div> | |
| """ | |
| else: | |
| return "" | |
| except Exception as e: | |
| return f""" | |
| <div style=" | |
| background: rgba(231, 76, 60, 0.1); | |
| border: 2px solid #E74C3C; | |
| border-radius: 12px; | |
| padding: 24px; | |
| margin: 20px 0; | |
| text-align: center; | |
| "> | |
| <h2 style="color: #E74C3C; margin: 0 0 12px 0;">Error</h2> | |
| <p style="font-size: 18px; color: #E0E0E0;">API request failed: {str(e)}</p> | |
| </div> | |
| """ | |
| tabs_css = """ | |
| /* Style all Gradio tab buttons */ | |
| button[role="tab"] { | |
| font-size: 14px !important; | |
| font-family: 'Montserrat', sans-serif !important; | |
| font-weight: 600 !important; | |
| padding: 12px 24px !important; | |
| margin: 0 6px !important; | |
| background-color: #0B0F19 !important; | |
| color: #F3F4F6 !important; | |
| border-radius: 8px !important; | |
| border: 1px solid #1a1a1a !important; | |
| box-shadow: none !important; | |
| transition: all 0.2s ease !important; | |
| } | |
| /* Style selected tab button */ | |
| button[role="tab"].selected { | |
| background-color: #635bff !important; | |
| color: white !important; | |
| box-shadow: 0 0 6px rgba(99, 91, 255, 0.5) !important; | |
| } | |
| /* Inactive tab style */ | |
| button[role="tab"]:not(.selected) { | |
| font-size: 14px !important; | |
| font-family: 'Montserrat', sans-serif !important; | |
| font-weight: 600 !important; | |
| padding: 12px 24px !important; | |
| margin: 0 6px !important; | |
| background-color: #9D2C53 !important; | |
| color: #F3F4F6 !important; | |
| border-radius: 8px !important; | |
| border: 1px solid #1a1a1a !important; | |
| box-shadow: none !important; | |
| transition: all 0.2s ease !important; | |
| } | |
| /* Optional: hover effect */ | |
| button[role="tab"]:hover { | |
| background-color: #1a1a2b !important; | |
| color: white !important; | |
| } | |
| """ | |
| with gr.Blocks(theme=gr.themes.Soft(), css=tabs_css) as Demo: | |
| header_box = gr.HTML(HTML_HEADER) | |
| with gr.Row(equal_height=True): | |
| with gr.Column(scale=1): | |
| input_audio_path = gr.Audio( | |
| label="Input Audio", | |
| type="filepath", | |
| sources=["upload", "microphone"] | |
| ) | |
| check_button = gr.Button("Check Audio!", variant="primary") | |
| with gr.Column(scale=1): | |
| verdict_box = gr.HTML("") | |
| check_button.click( | |
| check_audio_deepfake, | |
| inputs=input_audio_path, | |
| outputs=verdict_box, | |
| ) | |
| if __name__ == "__main__": | |
| Demo.launch() | |