mrfakename commited on
Commit
40d676f
·
verified ·
1 Parent(s): f9759b8

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +451 -0
app.py ADDED
@@ -0,0 +1,451 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ # from dotenv import load_dotenv
3
+ import os
4
+ from huggingface_hub import hf_hub_download
5
+ import pandas as pd
6
+ import sqlite3
7
+
8
+ # load_dotenv()
9
+
10
+ DB_DATASET_ID = os.getenv("DB_DATASET_ID")
11
+ DB_NAME = os.getenv("DB_NAME")
12
+
13
+ cache_path = hf_hub_download(repo_id=DB_DATASET_ID, repo_type='dataset', filename=DB_NAME, token=os.getenv("HF_TOKEN"))
14
+
15
+ # Model name mappings and metadata
16
+
17
+ closed_source = [
18
+ 'ElevenLabs',
19
+ 'Play.HT 2.0',
20
+ 'Play.HT 3.0 Mini',
21
+ 'PlayDialog',
22
+ 'Papla P1',
23
+ 'Hume Octave'
24
+ ]
25
+
26
+ # Model name mapping, can include models that users cannot vote on
27
+ model_names = {
28
+ 'styletts2': 'StyleTTS 2',
29
+ 'tacotron': 'Tacotron',
30
+ 'tacotronph': 'Tacotron Phoneme',
31
+ 'tacotrondca': 'Tacotron DCA',
32
+ 'speedyspeech': 'Speedy Speech',
33
+ 'overflow': 'Overflow TTS',
34
+ 'anonymoussparkle': 'Anonymous Sparkle',
35
+ 'vits': 'VITS',
36
+ 'vitsneon': 'VITS Neon',
37
+ 'neuralhmm': 'Neural HMM',
38
+ 'glow': 'Glow TTS',
39
+ 'fastpitch': 'FastPitch',
40
+ 'jenny': 'Jenny',
41
+ 'tortoise': 'Tortoise TTS',
42
+ 'xtts2': 'Coqui XTTSv2',
43
+ 'xtts': 'Coqui XTTS',
44
+ 'openvoice': 'MyShell OpenVoice',
45
+ 'elevenlabs': 'ElevenLabs',
46
+ 'openai': 'OpenAI',
47
+ 'hierspeech': 'HierSpeech++',
48
+ 'pheme': 'PolyAI Pheme',
49
+ 'speecht5': 'SpeechT5',
50
+ 'metavoice': 'MetaVoice-1B',
51
+ }
52
+ model_links = {
53
+ 'ElevenLabs': 'https://elevenlabs.io/',
54
+ 'Play.HT 2.0': 'https://play.ht/',
55
+ 'Play.HT 3.0 Mini': 'https://play.ht/',
56
+ 'XTTSv2': 'https://huggingface.co/coqui/XTTS-v2',
57
+ 'MeloTTS': 'https://github.com/myshell-ai/MeloTTS',
58
+ 'StyleTTS 2': 'https://github.com/yl4579/StyleTTS2',
59
+ 'Parler TTS Large': 'https://github.com/huggingface/parler-tts',
60
+ 'Parler TTS': 'https://github.com/huggingface/parler-tts',
61
+ 'Fish Speech v1.5': 'https://github.com/fishaudio/fish-speech',
62
+ 'Fish Speech v1.4': 'https://github.com/fishaudio/fish-speech',
63
+ 'GPT-SoVITS': 'https://github.com/RVC-Boss/GPT-SoVITS',
64
+ 'WhisperSpeech': 'https://github.com/WhisperSpeech/WhisperSpeech',
65
+ 'VoiceCraft 2.0': 'https://github.com/jasonppy/VoiceCraft',
66
+ 'PlayDialog': 'https://play.ht/',
67
+ 'Kokoro v0.19': 'https://huggingface.co/hexgrad/Kokoro-82M',
68
+ 'Kokoro v1.0': 'https://huggingface.co/hexgrad/Kokoro-82M',
69
+ 'CosyVoice 2.0': 'https://github.com/FunAudioLLM/CosyVoice',
70
+ 'MetaVoice': 'https://github.com/metavoiceio/metavoice-src',
71
+ 'OpenVoice': 'https://github.com/myshell-ai/OpenVoice',
72
+ 'OpenVoice V2': 'https://github.com/myshell-ai/OpenVoice',
73
+ 'Pheme': 'https://github.com/PolyAI-LDN/pheme',
74
+ 'Vokan TTS': 'https://huggingface.co/ShoukanLabs/Vokan',
75
+ 'Papla P1': 'https://papla.media',
76
+ 'Hume Octave': 'https://www.hume.ai'
77
+ }
78
+
79
+
80
+ def get_db():
81
+ conn = sqlite3.connect(cache_path)
82
+ return conn
83
+
84
+ def get_leaderboard(reveal_prelim=False, hide_battle_votes=False, sort_by_elo=True, hide_proprietary=False):
85
+ conn = get_db()
86
+ cursor = conn.cursor()
87
+
88
+ if hide_battle_votes:
89
+ sql = '''
90
+ SELECT m.name,
91
+ SUM(CASE WHEN v.username NOT LIKE '%_battle' AND v.vote = 1 THEN 1 ELSE 0 END) as upvote,
92
+ SUM(CASE WHEN v.username NOT LIKE '%_battle' AND v.vote = -1 THEN 1 ELSE 0 END) as downvote
93
+ FROM model m
94
+ LEFT JOIN vote v ON m.name = v.model
95
+ GROUP BY m.name
96
+ '''
97
+ else:
98
+ sql = '''
99
+ SELECT name,
100
+ SUM(CASE WHEN vote = 1 THEN 1 ELSE 0 END) as upvote,
101
+ SUM(CASE WHEN vote = -1 THEN 1 ELSE 0 END) as downvote
102
+ FROM model
103
+ LEFT JOIN vote ON model.name = vote.model
104
+ GROUP BY name
105
+ '''
106
+
107
+ cursor.execute(sql)
108
+ data = cursor.fetchall()
109
+ df = pd.DataFrame(data, columns=['name', 'upvote', 'downvote'])
110
+ df['name'] = df['name'].replace(model_names).replace('Anonymous Sparkle', 'Fish Speech v1.5')
111
+
112
+ # Calculate total votes and win rate
113
+ df['votes'] = df['upvote'] + df['downvote']
114
+ df['win_rate'] = (df['upvote'] / df['votes'] * 100).round(1)
115
+
116
+ # Remove models with no votes
117
+ df = df[df['votes'] > 0]
118
+
119
+ # Filter out rows with insufficient votes if not revealing preliminary results
120
+ if not reveal_prelim:
121
+ df = df[df['votes'] > 500]
122
+
123
+ ## Calculate ELO SCORE (kept as secondary metric)
124
+ df['elo'] = 1200
125
+ for i in range(len(df)):
126
+ for j in range(len(df)):
127
+ if i != j:
128
+ try:
129
+ expected_a = 1 / (1 + 10 ** ((df['elo'].iloc[j] - df['elo'].iloc[i]) / 400))
130
+ expected_b = 1 / (1 + 10 ** ((df['elo'].iloc[i] - df['elo'].iloc[j]) / 400))
131
+ actual_a = df['upvote'].iloc[i] / df['votes'].iloc[i] if df['votes'].iloc[i] > 0 else 0.5
132
+ actual_b = df['upvote'].iloc[j] / df['votes'].iloc[j] if df['votes'].iloc[j] > 0 else 0.5
133
+ df.iloc[i, df.columns.get_loc('elo')] += 32 * (actual_a - expected_a)
134
+ df.iloc[j, df.columns.get_loc('elo')] += 32 * (actual_b - expected_b)
135
+ except Exception as e:
136
+ print(f"Error in ELO calculation for rows {i} and {j}: {str(e)}")
137
+ continue
138
+ df['elo'] = round(df['elo'])
139
+
140
+ # Sort based on user preference
141
+ sort_column = 'elo' if sort_by_elo else 'win_rate'
142
+ df = df.sort_values(by=sort_column, ascending=False)
143
+ df['order'] = ['#' + str(i + 1) for i in range(len(df))]
144
+
145
+ # Select and order columns for display
146
+ df = df[['order', 'name', 'win_rate', 'votes', 'elo']]
147
+
148
+ # Remove proprietary models if filter is enabled
149
+ if hide_proprietary:
150
+ df = df[~df['name'].isin(closed_source)]
151
+
152
+ # Convert DataFrame to markdown table with CSS styling
153
+ markdown_table = """
154
+ <style>
155
+ /* Reset any Gradio table styles */
156
+ .leaderboard-table,
157
+ .leaderboard-table th,
158
+ .leaderboard-table td {
159
+ border: none !important;
160
+ border-collapse: separate !important;
161
+ border-spacing: 0 !important;
162
+ }
163
+
164
+ .leaderboard-container {
165
+ background: var(--background-fill-primary);
166
+ border: 1px solid var(--border-color-primary);
167
+ border-radius: 12px;
168
+ padding: 4px;
169
+ margin: 10px 0;
170
+ width: 100%;
171
+ overflow-x: auto; /* Enable horizontal scroll */
172
+ }
173
+
174
+ .leaderboard-scroll {
175
+ max-height: 600px;
176
+ overflow-y: auto;
177
+ border-radius: 8px;
178
+ }
179
+
180
+ .leaderboard-table {
181
+ width: 100%;
182
+ border-spacing: 0;
183
+ border-collapse: separate;
184
+ font-size: 15px;
185
+ line-height: 1.5;
186
+ table-layout: auto; /* Allow flexible column widths */
187
+ }
188
+
189
+ .leaderboard-table th {
190
+ background: var(--background-fill-secondary);
191
+ color: var(--body-text-color);
192
+ font-weight: 600;
193
+ text-align: left;
194
+ padding: 12px 16px;
195
+ position: sticky;
196
+ top: 0;
197
+ z-index: 1;
198
+ }
199
+
200
+ .leaderboard-table th:after {
201
+ content: '';
202
+ position: absolute;
203
+ left: 0;
204
+ bottom: 0;
205
+ width: 100%;
206
+ border-bottom: 1px solid var(--border-color-primary);
207
+ }
208
+
209
+ .leaderboard-table td {
210
+ padding: 12px 16px;
211
+ color: var(--body-text-color);
212
+ }
213
+
214
+ .leaderboard-table tr td {
215
+ border-bottom: 1px solid var(--border-color-primary);
216
+ }
217
+
218
+ .leaderboard-table tr:last-child td {
219
+ border-bottom: none;
220
+ }
221
+
222
+ .leaderboard-table tr:hover td {
223
+ background: var(--background-fill-secondary);
224
+ }
225
+
226
+ /* Column-specific styles */
227
+ .leaderboard-table .col-rank {
228
+ width: 70px;
229
+ min-width: 70px; /* Prevent rank from shrinking */
230
+ }
231
+
232
+ .leaderboard-table .col-model {
233
+ min-width: 200px; /* Minimum width before scrolling */
234
+ }
235
+
236
+ .leaderboard-table .col-winrate {
237
+ width: 100px;
238
+ min-width: 100px; /* Prevent win rate from shrinking */
239
+ }
240
+
241
+ .leaderboard-table .col-votes {
242
+ width: 100px;
243
+ min-width: 100px; /* Prevent votes from shrinking */
244
+ }
245
+
246
+ .leaderboard-table .col-arena {
247
+ width: 100px;
248
+ min-width: 100px; /* Prevent arena score from shrinking */
249
+ }
250
+
251
+ .win-rate {
252
+ display: inline-block;
253
+ font-weight: 600;
254
+ padding: 4px 8px;
255
+ border-radius: 6px;
256
+ min-width: 65px;
257
+ text-align: center;
258
+ }
259
+
260
+ .win-rate-excellent {
261
+ background-color: var(--color-accent);
262
+ color: var(--color-accent-foreground);
263
+ }
264
+
265
+ .win-rate-good {
266
+ background-color: var(--color-accent-soft);
267
+ color: var(--body-text-color);
268
+ }
269
+
270
+ .win-rate-average {
271
+ background-color: var(--background-fill-secondary);
272
+ color: var(--body-text-color);
273
+ border: 1px solid var(--border-color-primary);
274
+ }
275
+
276
+ .win-rate-below {
277
+ background-color: var(--error-background-fill);
278
+ color: var(--body-text-color);
279
+ }
280
+
281
+ .model-link {
282
+ color: var(--body-text-color) !important;
283
+ text-decoration: none !important;
284
+ border-bottom: 2px dashed rgba(128, 128, 128, 0.3);
285
+ }
286
+
287
+ .model-link:hover {
288
+ color: var(--color-accent) !important;
289
+ border-bottom-color: var(--color-accent) !important;
290
+ }
291
+
292
+ .proprietary-badge {
293
+ display: inline-block;
294
+ font-size: 12px;
295
+ padding: 2px 6px;
296
+ border-radius: 4px;
297
+ background-color: var(--background-fill-secondary);
298
+ color: var(--body-text-color);
299
+ margin-left: 6px;
300
+ border: 1px solid var(--border-color-primary);
301
+ }
302
+
303
+ /* New Arena V2 Pointer */
304
+ .arena-v2-pointer {
305
+ display: block;
306
+ margin: 20px auto;
307
+ padding: 20px;
308
+ text-align: center;
309
+ border-radius: 12px;
310
+ font-size: 20px;
311
+ font-weight: bold;
312
+ cursor: pointer;
313
+ transition: all 0.3s ease;
314
+ position: relative;
315
+ overflow: hidden;
316
+ text-decoration: none !important;
317
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
318
+ max-width: 800px;
319
+ background: linear-gradient(135deg, #FF7B00, #FF5500);
320
+ color: white !important;
321
+ border: none;
322
+ }
323
+
324
+ /* Dark mode adjustments */
325
+ @media (prefers-color-scheme: dark) {
326
+ .arena-v2-pointer {
327
+ box-shadow: 0 4px 20px rgba(255, 123, 0, 0.3);
328
+ }
329
+ }
330
+
331
+ .arena-v2-pointer:hover {
332
+ transform: translateY(-5px);
333
+ box-shadow: 0 7px 25px rgba(255, 123, 0, 0.4);
334
+ filter: brightness(1.05);
335
+ color: white !important;
336
+ text-decoration: none !important;
337
+ }
338
+
339
+ .arena-v2-pointer::after {
340
+ content: "→";
341
+ font-size: 24px;
342
+ margin-left: 10px;
343
+ display: inline-block;
344
+ transition: transform 0.3s ease;
345
+ }
346
+
347
+ .arena-v2-pointer:hover::after {
348
+ transform: translateX(5px);
349
+ }
350
+ </style>
351
+
352
+ <a href="https://huggingface.co/spaces/TTS-AGI/TTS-Arena-V2" class="arena-v2-pointer" target="_blank">
353
+ Visit the new TTS Arena V2 to vote on the latest models!
354
+ </a>
355
+
356
+ <div class="leaderboard-container">
357
+ <div class="leaderboard-scroll">
358
+ <table class="leaderboard-table">
359
+ <thead>
360
+ <tr>
361
+ <th class="col-rank">Rank</th>
362
+ <th class="col-model">Model</th>
363
+ <th class="col-winrate">Win Rate</th>
364
+ <th class="col-votes">Votes</th>
365
+ """ + ("""<th class="col-arena">Arena Score</th>""" if sort_by_elo else "") + """
366
+ </tr>
367
+ </thead>
368
+ <tbody>
369
+ """
370
+
371
+ def get_win_rate_class(win_rate):
372
+ if win_rate >= 60:
373
+ return "win-rate-excellent"
374
+ elif win_rate >= 55:
375
+ return "win-rate-good"
376
+ elif win_rate >= 45:
377
+ return "win-rate-average"
378
+ else:
379
+ return "win-rate-below"
380
+
381
+ for _, row in df.iterrows():
382
+ win_rate_class = get_win_rate_class(row['win_rate'])
383
+ win_rate_html = f'<span class="win-rate {win_rate_class}">{row["win_rate"]}%</span>'
384
+
385
+ # Add link to model name if available and proprietary badge if closed source
386
+ model_name = row['name']
387
+ original_model_name = model_name
388
+ if model_name in model_links:
389
+ model_name = f'<a href="{model_links[model_name]}" target="_blank" class="model-link">{model_name}</a>'
390
+
391
+ if original_model_name in closed_source:
392
+ model_name += '<span class="proprietary-badge">Proprietary</span>'
393
+
394
+ markdown_table += f'''<tr>
395
+ <td class="col-rank">{row['order']}</td>
396
+ <td class="col-model">{model_name}</td>
397
+ <td class="col-winrate">{win_rate_html}</td>
398
+ <td class="col-votes">{row['votes']:,}</td>''' + (
399
+ f'''<td class="col-arena">{int(row['elo'])}</td>''' if sort_by_elo else ""
400
+ ) + "</tr>\n"
401
+
402
+ markdown_table += "</tbody></table></div></div>"
403
+ return markdown_table
404
+
405
+ ABOUT = """
406
+ # TTS Arena (Legacy)
407
+
408
+ This is the legacy read-only leaderboard for TTS Arena V1. No new votes are being accepted.
409
+
410
+ **Please visit the new [TTS Arena](https://huggingface.co/spaces/TTS-AGI/TTS-Arena-V2) to vote!**
411
+ """
412
+
413
+ CITATION_TEXT = """@misc{tts-arena,
414
+ title = {Text to Speech Arena},
415
+ author = {mrfakename and Srivastav, Vaibhav and Fourrier, Clémentine and Pouget, Lucain and Lacombe, Yoach and main and Gandhi, Sanchit},
416
+ year = 2024,
417
+ publisher = {Hugging Face},
418
+ howpublished = "\\url{https://huggingface.co/spaces/TTS-AGI/TTS-Arena}"
419
+ }"""
420
+ FOOTER = f"""
421
+ If you reference the Arena in your work, please cite it as follows:
422
+
423
+ ```bibtex
424
+ {CITATION_TEXT}
425
+ ```
426
+ """
427
+
428
+ with gr.Blocks() as demo:
429
+ gr.Markdown(ABOUT)
430
+
431
+ with gr.Row():
432
+ with gr.Column():
433
+ reveal_prelim = gr.Checkbox(label="Show preliminary results (< 500 votes)", value=False)
434
+ hide_battle_votes = gr.Checkbox(label="Exclude battle votes", value=False)
435
+ with gr.Column():
436
+ sort_by_elo = gr.Checkbox(label="Sort by Arena Score instead of Win Rate", value=True)
437
+ hide_proprietary = gr.Checkbox(label="Hide proprietary models", value=False)
438
+
439
+ leaderboard_html = gr.HTML(get_leaderboard())
440
+
441
+ # Update leaderboard when filters change
442
+ for control in [reveal_prelim, hide_battle_votes, sort_by_elo, hide_proprietary]:
443
+ control.change(
444
+ fn=get_leaderboard,
445
+ inputs=[reveal_prelim, hide_battle_votes, sort_by_elo, hide_proprietary],
446
+ outputs=leaderboard_html
447
+ )
448
+
449
+ gr.Markdown(FOOTER)
450
+
451
+ demo.launch()