ds-ekaterina commited on
Commit
7099c03
·
0 Parent(s):

Initial commit

Browse files
Files changed (4) hide show
  1. .gitattributes +35 -0
  2. .gitignore +1 -0
  3. README.md +13 -0
  4. app.py +246 -0
.gitattributes ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1 @@
 
 
1
+ audio_examples/*.wav
README.md ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: DeepfakeAudioDetection
3
+ emoji: 🔥
4
+ colorFrom: purple
5
+ colorTo: blue
6
+ sdk: gradio
7
+ sdk_version: 6.9.0
8
+ app_file: app.py
9
+ pinned: false
10
+ short_description: Real-time audio deepfake detection demo.
11
+ ---
12
+
13
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
app.py ADDED
@@ -0,0 +1,246 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import time
3
+ import requests
4
+ import gradio as gr
5
+
6
+ HTML_HEADER = """
7
+ <header style="text-align: center; padding: 20px; border-bottom: 2px solid #cc3300;">
8
+ <h1>Demo of Audio Deepfake Detection</h1>
9
+ <p style="font-size: 18px;">
10
+ To learn more, visit our website: <a href="https://dataspike.io/" target="_blank" style="font-size: 20px; text-decoration: none;">
11
+ https://dataspike.io/ </a>
12
+ </p>
13
+ <img src="https://api.visitorbadge.io/api/visitors?path=https%3A%2F%2Fhuggingface.co%2Fspaces%2Fdataspike%2FAudio-Deepfake-Detection&countColor=%23263759"
14
+ alt="Visitor Count"
15
+ style="display: none;">
16
+ </header>
17
+ """
18
+
19
+ def get_verdict_html(score, verdict):
20
+ """Generate HTML verdict block based on score and verdict."""
21
+
22
+ # Determine color and label based on verdict
23
+ if verdict == "deepfake_likely":
24
+ color = "#E74C3C"
25
+ label = "Deepfake"
26
+ description = "This audio shows strong signs of manipulation (likely a deepfake)."
27
+ elif verdict == "deepfake_unlikely":
28
+ color = "#2ECC71"
29
+ label = "Not Deepfake"
30
+ description = "This audio appears to be genuine."
31
+ else:
32
+ color = "#F39C12"
33
+ label = "Uncertain"
34
+ description = "The analysis is inconclusive. Further verification may be needed."
35
+
36
+ # Convert score to percentage
37
+ percentage = int(score * 100)
38
+
39
+ html = f"""
40
+ <div style="
41
+ background: linear-gradient(135deg, {color}15 0%, {color}05 100%);
42
+ border: 2px solid {color};
43
+ border-radius: 12px;
44
+ padding: 24px;
45
+ margin: 20px 0;
46
+ text-align: center;
47
+ ">
48
+ <h2 style="color: {color}; margin: 0 0 12px 0; font-size: 32px;">{label}</h2>
49
+ <p style="font-size: 18px; color: #E0E0E0; margin: 12px 0;">{description}</p>
50
+ <div style="
51
+ background: rgba(0,0,0,0.3);
52
+ border-radius: 8px;
53
+ padding: 16px;
54
+ margin-top: 16px;
55
+ ">
56
+ <p style="font-size: 16px; color: #B0B0B0; margin: 0;">Deepfake Probability</p>
57
+ <p style="font-size: 36px; font-weight: bold; color: {color}; margin: 8px 0 0 0;">{percentage}%</p>
58
+ </div>
59
+ </div>
60
+ """
61
+ return html
62
+
63
+ def api_status(job_id, poll_interval=1.0, max_attempts=120):
64
+ """Poll API for job status until completion."""
65
+ url = f"https://api.dataspike.io/api/v4/deepfake/job/{job_id}"
66
+ headers = {"ds-api-token": os.getenv("API_KEY")}
67
+
68
+ for attempt in range(max_attempts):
69
+ try:
70
+ resp = requests.get(url, headers=headers, timeout=10)
71
+ result = resp.json()
72
+ status = result.get("status", "")
73
+
74
+ if status in ("completed", "done", "error", "failed"):
75
+ return result
76
+
77
+ time.sleep(poll_interval)
78
+ except Exception as e:
79
+ # If polling fails, return error
80
+ return {
81
+ "status": "error",
82
+ "errors": [f"Polling failed: {str(e)}"]
83
+ }
84
+
85
+ # If max attempts reached
86
+ return {
87
+ "status": "error",
88
+ "errors": ["Timeout: processing took too long"]
89
+ }
90
+
91
+
92
+ def check_audio_deepfake(file_path):
93
+ """Check if audio is a deepfake using the API."""
94
+ if not file_path:
95
+ return """
96
+ <div style="
97
+ background: rgba(231, 76, 60, 0.1);
98
+ border: 2px solid #E74C3C;
99
+ border-radius: 12px;
100
+ padding: 24px;
101
+ margin: 20px 0;
102
+ text-align: center;
103
+ ">
104
+ <h2 style="color: #E74C3C; margin: 0 0 12px 0;">No Audio File</h2>
105
+ <p style="font-size: 18px; color: #E0E0E0;">Please submit an audio file first and then click the button 'Check Audio!'</p>
106
+ </div>
107
+ """
108
+
109
+ url = "https://api.dataspike.io/api/v4/deepfake/audio/analyze"
110
+ headers = {"ds-api-token": os.getenv("API_KEY")}
111
+
112
+ try:
113
+ # Step 1: Submit audio file and get job_id
114
+ with open(file_path, "rb") as f:
115
+ files = {"file": f}
116
+ response = requests.post(url, headers=headers, files=files)
117
+
118
+ job_response = response.json()
119
+
120
+ # Check if we got a job_id
121
+ if "id" not in job_response:
122
+ return """
123
+ <div style="
124
+ background: rgba(231, 76, 60, 0.1);
125
+ border: 2px solid #E74C3C;
126
+ border-radius: 12px;
127
+ padding: 24px;
128
+ margin: 20px 0;
129
+ text-align: center;
130
+ ">
131
+ <h2 style="color: #E74C3C; margin: 0 0 12px 0;">Upload Failed</h2>
132
+ <p style="font-size: 18px; color: #E0E0E0;">Failed to upload audio file. Please try again.</p>
133
+ </div>
134
+ """
135
+
136
+ job_id = job_response["id"]
137
+
138
+ # Step 2: Poll for results
139
+ deepfake_result = api_status(job_id)
140
+
141
+ # Check verdict and show appropriate message
142
+ if deepfake_result.get("status") in ("completed", "done"):
143
+ score = deepfake_result.get("score", 0)
144
+ verdict = deepfake_result.get("verdict", "unknown")
145
+ return get_verdict_html(score, verdict)
146
+ elif deepfake_result.get("status") in ("error", "failed"):
147
+ return """
148
+ <div style="
149
+ background: rgba(231, 76, 60, 0.1);
150
+ border: 2px solid #E74C3C;
151
+ border-radius: 12px;
152
+ padding: 24px;
153
+ margin: 20px 0;
154
+ text-align: center;
155
+ ">
156
+ <h2 style="color: #E74C3C; margin: 0 0 12px 0;">Detection Failed</h2>
157
+ <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>
158
+ </div>
159
+ """
160
+ else:
161
+ return ""
162
+
163
+ except Exception as e:
164
+ return f"""
165
+ <div style="
166
+ background: rgba(231, 76, 60, 0.1);
167
+ border: 2px solid #E74C3C;
168
+ border-radius: 12px;
169
+ padding: 24px;
170
+ margin: 20px 0;
171
+ text-align: center;
172
+ ">
173
+ <h2 style="color: #E74C3C; margin: 0 0 12px 0;">Error</h2>
174
+ <p style="font-size: 18px; color: #E0E0E0;">API request failed: {str(e)}</p>
175
+ </div>
176
+ """
177
+
178
+
179
+ tabs_css = """
180
+ /* Style all Gradio tab buttons */
181
+ button[role="tab"] {
182
+ font-size: 14px !important;
183
+ font-family: 'Montserrat', sans-serif !important;
184
+ font-weight: 600 !important;
185
+ padding: 12px 24px !important;
186
+ margin: 0 6px !important;
187
+ background-color: #0B0F19 !important;
188
+ color: #F3F4F6 !important;
189
+ border-radius: 8px !important;
190
+ border: 1px solid #1a1a1a !important;
191
+ box-shadow: none !important;
192
+ transition: all 0.2s ease !important;
193
+ }
194
+
195
+ /* Style selected tab button */
196
+ button[role="tab"].selected {
197
+ background-color: #635bff !important;
198
+ color: white !important;
199
+ box-shadow: 0 0 6px rgba(99, 91, 255, 0.5) !important;
200
+ }
201
+
202
+ /* Inactive tab style */
203
+ button[role="tab"]:not(.selected) {
204
+ font-size: 14px !important;
205
+ font-family: 'Montserrat', sans-serif !important;
206
+ font-weight: 600 !important;
207
+ padding: 12px 24px !important;
208
+ margin: 0 6px !important;
209
+ background-color: #9D2C53 !important;
210
+ color: #F3F4F6 !important;
211
+ border-radius: 8px !important;
212
+ border: 1px solid #1a1a1a !important;
213
+ box-shadow: none !important;
214
+ transition: all 0.2s ease !important;
215
+ }
216
+
217
+ /* Optional: hover effect */
218
+ button[role="tab"]:hover {
219
+ background-color: #1a1a2b !important;
220
+ color: white !important;
221
+ }
222
+ """
223
+
224
+
225
+ with gr.Blocks(theme=gr.themes.Soft(), css=tabs_css) as Demo:
226
+ header_box = gr.HTML(HTML_HEADER)
227
+ with gr.Row(equal_height=True):
228
+ with gr.Column(scale=1):
229
+ input_audio_path = gr.Audio(
230
+ label="Input Audio",
231
+ type="filepath",
232
+ sources=["upload", "microphone"]
233
+ )
234
+ check_button = gr.Button("Check Audio!", variant="primary")
235
+ with gr.Column(scale=1):
236
+ verdict_box = gr.HTML("")
237
+
238
+ check_button.click(
239
+ check_audio_deepfake,
240
+ inputs=input_audio_path,
241
+ outputs=verdict_box,
242
+ )
243
+
244
+
245
+ if __name__ == "__main__":
246
+ Demo.launch()