Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
# app.py β DeepFake AI Forensics (
|
| 2 |
import os
|
| 3 |
import subprocess
|
| 4 |
import tempfile
|
|
@@ -30,24 +30,55 @@ model.to(device)
|
|
| 30 |
print(f"[+] Model loaded on {device}")
|
| 31 |
|
| 32 |
# ==========================================
|
| 33 |
-
# 2. AUDIO / VIDEO PREPROCESSING
|
| 34 |
# ==========================================
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
def convert_to_audio(file_path):
|
| 36 |
ext = os.path.splitext(file_path)[1].lower().lstrip('.')
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
return
|
| 50 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
|
| 52 |
def load_audio(path):
|
| 53 |
audio, _ = librosa.load(path, sr=16000)
|
|
@@ -82,19 +113,19 @@ def analyze(file_path):
|
|
| 82 |
if ai_score < 0.35:
|
| 83 |
verdict = "HUMAN VOICE"
|
| 84 |
level = "LOW RISK"
|
| 85 |
-
color = "#059669"
|
| 86 |
icon = "π§"
|
| 87 |
glow = "rgba(5,150,105,0.18)"
|
| 88 |
elif ai_score < 0.65:
|
| 89 |
verdict = "UNCERTAIN / MIXED"
|
| 90 |
level = "MEDIUM RISK"
|
| 91 |
-
color = "#d97706"
|
| 92 |
icon = "β οΈ"
|
| 93 |
glow = "rgba(217,119,6,0.18)"
|
| 94 |
else:
|
| 95 |
verdict = "AI / SYNTHETIC VOICE"
|
| 96 |
level = "HIGH RISK"
|
| 97 |
-
color = "#dc2626"
|
| 98 |
icon = "π€"
|
| 99 |
glow = "rgba(220,38,38,0.18)"
|
| 100 |
|
|
@@ -262,7 +293,12 @@ def detect_audio(audio_file):
|
|
| 262 |
return plot_path, result_html, f"{percentage:.1f}%", color, ""
|
| 263 |
|
| 264 |
except Exception as e:
|
| 265 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 266 |
|
| 267 |
|
| 268 |
def detect_video(video_file):
|
|
@@ -347,7 +383,12 @@ def detect_video(video_file):
|
|
| 347 |
return plot_path, result_html, f"{percentage:.1f}%", color, ""
|
| 348 |
|
| 349 |
except Exception as e:
|
| 350 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 351 |
|
| 352 |
|
| 353 |
# ==========================================
|
|
@@ -497,11 +538,15 @@ def build_ui():
|
|
| 497 |
<span class="format-badge">M4A</span>
|
| 498 |
<span class="format-badge">FLAC</span>
|
| 499 |
<span class="format-badge">OGG</span>
|
|
|
|
| 500 |
</div>
|
| 501 |
<div style="font-size: 0.8em; color: #475569; margin-bottom: 16px; display: flex; align-items: center; gap: 6px;">
|
| 502 |
<span style="font-size: 1.2em;">π</span>
|
| 503 |
<span>Maximum file size: <strong style="color: #1e40af;">50 MB</strong></span>
|
| 504 |
</div>
|
|
|
|
|
|
|
|
|
|
| 505 |
""")
|
| 506 |
|
| 507 |
audio_input = gr.Audio(
|
|
|
|
| 1 |
+
# app.py β DeepFake AI Forensics (WhatsApp Voice Note Fix)
|
| 2 |
import os
|
| 3 |
import subprocess
|
| 4 |
import tempfile
|
|
|
|
| 30 |
print(f"[+] Model loaded on {device}")
|
| 31 |
|
| 32 |
# ==========================================
|
| 33 |
+
# 2. AUDIO / VIDEO PREPROCESSING (ROBUST)
|
| 34 |
# ==========================================
|
| 35 |
+
def normalize_audio(file_path):
|
| 36 |
+
"""
|
| 37 |
+
Converts ANY audio/video to standard 16kHz mono WAV via FFmpeg.
|
| 38 |
+
This fixes WhatsApp voice notes (Opus/OGG disguised as MP3),
|
| 39 |
+
corrupt headers, and exotic codecs.
|
| 40 |
+
"""
|
| 41 |
+
out = tempfile.mktemp(suffix=".wav")
|
| 42 |
+
cmd = [
|
| 43 |
+
"ffmpeg", "-y",
|
| 44 |
+
"-i", file_path,
|
| 45 |
+
"-vn", # no video
|
| 46 |
+
"-acodec", "pcm_s16le", # 16-bit PCM
|
| 47 |
+
"-ar", "16000", # 16 kHz
|
| 48 |
+
"-ac", "1", # mono
|
| 49 |
+
"-af", "loudnorm=I=-16:TP=-1.5:LRA=11", # normalize levels
|
| 50 |
+
out
|
| 51 |
+
]
|
| 52 |
+
result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
| 53 |
+
|
| 54 |
+
if result.returncode != 0:
|
| 55 |
+
err = result.stderr.decode('utf-8', errors='ignore')[:200]
|
| 56 |
+
raise RuntimeError(f"FFmpeg could not decode this file. It may be corrupted or use an unsupported codec.\nDetails: {err}")
|
| 57 |
+
|
| 58 |
+
return out
|
| 59 |
+
|
| 60 |
def convert_to_audio(file_path):
|
| 61 |
ext = os.path.splitext(file_path)[1].lower().lstrip('.')
|
| 62 |
+
|
| 63 |
+
# Supported audio formats (including WhatsApp Opus)
|
| 64 |
+
audio_exts = ["wav", "mp3", "flac", "m4a", "ogg", "opus", "aac", "wma", "oga"]
|
| 65 |
+
# Supported video formats
|
| 66 |
+
video_exts = ["mp4", "mkv", "avi", "mov", "webm", "flv", "3gp"]
|
| 67 |
+
|
| 68 |
+
if ext in audio_exts:
|
| 69 |
+
print(f"[+] Audio detected ({ext}) β normalizing via ffmpeg...")
|
| 70 |
+
return normalize_audio(file_path)
|
| 71 |
+
|
| 72 |
+
if ext in video_exts:
|
| 73 |
+
print("[+] Video detected β extracting & normalizing audio via ffmpeg...")
|
| 74 |
+
return normalize_audio(file_path)
|
| 75 |
+
|
| 76 |
+
# Unknown extension? Try ffmpeg anyway as last resort
|
| 77 |
+
print(f"[+] Unknown format ({ext}) β attempting ffmpeg decode...")
|
| 78 |
+
try:
|
| 79 |
+
return normalize_audio(file_path)
|
| 80 |
+
except Exception:
|
| 81 |
+
raise ValueError(f"Unsupported file format: {ext}. Please upload MP3, WAV, M4A, OGG, OPUS, or MP4.")
|
| 82 |
|
| 83 |
def load_audio(path):
|
| 84 |
audio, _ = librosa.load(path, sr=16000)
|
|
|
|
| 113 |
if ai_score < 0.35:
|
| 114 |
verdict = "HUMAN VOICE"
|
| 115 |
level = "LOW RISK"
|
| 116 |
+
color = "#059669"
|
| 117 |
icon = "π§"
|
| 118 |
glow = "rgba(5,150,105,0.18)"
|
| 119 |
elif ai_score < 0.65:
|
| 120 |
verdict = "UNCERTAIN / MIXED"
|
| 121 |
level = "MEDIUM RISK"
|
| 122 |
+
color = "#d97706"
|
| 123 |
icon = "β οΈ"
|
| 124 |
glow = "rgba(217,119,6,0.18)"
|
| 125 |
else:
|
| 126 |
verdict = "AI / SYNTHETIC VOICE"
|
| 127 |
level = "HIGH RISK"
|
| 128 |
+
color = "#dc2626"
|
| 129 |
icon = "π€"
|
| 130 |
glow = "rgba(220,38,38,0.18)"
|
| 131 |
|
|
|
|
| 293 |
return plot_path, result_html, f"{percentage:.1f}%", color, ""
|
| 294 |
|
| 295 |
except Exception as e:
|
| 296 |
+
err_msg = str(e)
|
| 297 |
+
if "FFmpeg" in err_msg:
|
| 298 |
+
err_html = f"<div style='color:#dc2626;padding:30px;'><strong>β File Decode Error</strong><br><br>{err_msg}<br><br><span style='color:#475569;font-size:0.9em;'>WhatsApp voice notes are often .opus or .ogg files disguised as .mp3. Try renaming the file to .ogg or exporting it differently.</span></div>"
|
| 299 |
+
else:
|
| 300 |
+
err_html = f"<div style='color:#dc2626;padding:30px;'>β Analysis Error: {err_msg}</div>"
|
| 301 |
+
return None, err_html, "Error", "#dc2626", ""
|
| 302 |
|
| 303 |
|
| 304 |
def detect_video(video_file):
|
|
|
|
| 383 |
return plot_path, result_html, f"{percentage:.1f}%", color, ""
|
| 384 |
|
| 385 |
except Exception as e:
|
| 386 |
+
err_msg = str(e)
|
| 387 |
+
if "FFmpeg" in err_msg:
|
| 388 |
+
err_html = f"<div style='color:#dc2626;padding:30px;'><strong>β File Decode Error</strong><br><br>{err_msg}</div>"
|
| 389 |
+
else:
|
| 390 |
+
err_html = f"<div style='color:#dc2626;padding:30px;'>β Analysis Error: {err_msg}</div>"
|
| 391 |
+
return None, err_html, "Error", "#dc2626", ""
|
| 392 |
|
| 393 |
|
| 394 |
# ==========================================
|
|
|
|
| 538 |
<span class="format-badge">M4A</span>
|
| 539 |
<span class="format-badge">FLAC</span>
|
| 540 |
<span class="format-badge">OGG</span>
|
| 541 |
+
<span class="format-badge">OPUS</span>
|
| 542 |
</div>
|
| 543 |
<div style="font-size: 0.8em; color: #475569; margin-bottom: 16px; display: flex; align-items: center; gap: 6px;">
|
| 544 |
<span style="font-size: 1.2em;">π</span>
|
| 545 |
<span>Maximum file size: <strong style="color: #1e40af;">50 MB</strong></span>
|
| 546 |
</div>
|
| 547 |
+
<div style="font-size: 0.75em; color: #94a3b8; background: #eff6ff; border-radius: 8px; padding: 10px 12px; margin-bottom: 12px; line-height: 1.5;">
|
| 548 |
+
π‘ <strong>WhatsApp voice notes:</strong> If your file fails to upload, try renaming it from <code>.mp3</code> to <code>.ogg</code> or <code>.opus</code> before uploading.
|
| 549 |
+
</div>
|
| 550 |
""")
|
| 551 |
|
| 552 |
audio_input = gr.Audio(
|