mehdilaalali commited on
Commit
ad19b2a
Β·
verified Β·
1 Parent(s): 3b6cb8e

style: completely overhaul UI aesthetic (glassmorphism) and fix yt-dlp youtube blocks

Browse files
Files changed (1) hide show
  1. app.py +126 -65
app.py CHANGED
@@ -135,10 +135,17 @@ def clone_voice(audio_path, url_input, voice_name, gender, languages_str):
135
  '-t', '60' # Limit to first 60 seconds to avoid exceeding API limits
136
  ],
137
  }
138
- with yt_dlp.YoutubeDL(ydl_opts) as ydl:
139
- info = ydl.extract_info(url_input.strip(), download=True)
140
- # after postprocessing, file has .mp3 extension
141
- final_audio_path = base_out + '.mp3'
 
 
 
 
 
 
 
142
 
143
  client = get_client()
144
  sample_b64 = base64.b64encode(Path(final_audio_path).read_bytes()).decode()
@@ -172,121 +179,175 @@ LANGUAGES = [
172
  ]
173
 
174
  css = """
175
- @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
176
 
177
- * { font-family: 'Inter', sans-serif; }
178
 
179
  body, .gradio-container {
180
- background: linear-gradient(135deg, #0f0c29, #302b63, #24243e) !important;
181
  min-height: 100vh;
182
  }
183
 
184
  .gradio-container {
185
- max-width: 900px !important;
186
  margin: 0 auto !important;
187
  }
188
 
189
- /* Header */
190
  .app-header {
191
  text-align: center;
192
- padding: 2.5rem 1rem 1rem;
193
- background: transparent;
 
194
  }
195
  .app-header h1 {
196
- font-size: 2.8rem;
197
- font-weight: 700;
198
- background: linear-gradient(90deg, #a78bfa, #60a5fa, #34d399);
 
199
  -webkit-background-clip: text;
200
  -webkit-text-fill-color: transparent;
201
  background-clip: text;
202
- margin-bottom: 0.3rem;
 
203
  }
204
  .app-header p {
205
  color: #94a3b8;
206
- font-size: 1.05rem;
 
207
  margin-top: 0;
208
  }
209
 
 
 
 
 
 
 
 
 
 
 
 
210
  /* Tabs */
 
 
 
 
211
  .tab-nav button {
212
- background: rgba(255,255,255,0.05) !important;
213
- border: 1px solid rgba(167,139,250,0.2) !important;
214
- color: #94a3b8 !important;
215
- border-radius: 10px !important;
216
- margin: 0 4px !important;
217
- font-weight: 500 !important;
218
- transition: all 0.2s ease !important;
 
 
 
 
219
  }
220
  .tab-nav button.selected, .tab-nav button:hover {
221
- background: linear-gradient(135deg, rgba(167,139,250,0.3), rgba(96,165,250,0.3)) !important;
222
- color: #e2e8f0 !important;
223
- border-color: #a78bfa !important;
224
- box-shadow: 0 0 15px rgba(167,139,250,0.3) !important;
225
  }
226
 
227
- /* Panels */
228
- .panel-box {
229
- background: rgba(255,255,255,0.04);
230
- border: 1px solid rgba(167,139,250,0.15);
231
- border-radius: 16px;
232
- padding: 1.5rem;
233
- margin: 1rem 0;
234
- backdrop-filter: blur(10px);
235
- }
236
-
237
- /* Inputs */
238
- textarea, input[type="text"] {
239
- background: rgba(255,255,255,0.06) !important;
240
- border: 1px solid rgba(167,139,250,0.25) !important;
241
- border-radius: 10px !important;
242
- color: #e2e8f0 !important;
243
- font-size: 0.95rem !important;
244
  }
245
  textarea:focus, input[type="text"]:focus {
246
- border-color: #a78bfa !important;
247
- box-shadow: 0 0 0 3px rgba(167,139,250,0.15) !important;
 
248
  }
249
 
250
- /* Buttons */
 
 
 
 
 
 
 
251
  button.primary {
252
- background: linear-gradient(135deg, #7c3aed, #2563eb) !important;
253
  border: none !important;
254
- border-radius: 10px !important;
255
  color: white !important;
256
- font-weight: 600 !important;
257
- padding: 0.65rem 1.5rem !important;
258
- transition: all 0.2s ease !important;
259
- box-shadow: 0 4px 15px rgba(124,58,237,0.4) !important;
 
 
 
260
  }
261
  button.primary:hover {
262
- transform: translateY(-2px) !important;
263
- box-shadow: 0 6px 20px rgba(124,58,237,0.5) !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
264
  }
265
 
266
- /* Status / output text */
267
  .status-text {
268
- font-size: 0.9rem;
269
- color: #94a3b8;
270
- font-style: italic;
 
 
 
 
271
  }
272
 
273
- /* Labels */
274
  label span {
275
  color: #cbd5e1 !important;
276
  font-weight: 500 !important;
 
277
  }
278
 
279
- /* Audio components */
280
  .audio-component {
281
- border-radius: 12px !important;
282
  overflow: hidden !important;
 
 
 
 
 
 
 
283
  }
 
 
 
284
  """
285
 
286
  # Initialize voices at startup
287
  INITIAL_VOICES = get_voice_choices()
288
 
289
- with gr.Blocks(title="Voxtral Studio β€” Mistral AI Audio") as demo:
290
 
291
  gr.HTML("""
292
  <div class="app-header">
@@ -332,7 +393,7 @@ with gr.Blocks(title="Voxtral Studio β€” Mistral AI Audio") as demo:
332
  )
333
 
334
  # ── TAB 2: Text to Speech ──────────────────────────────────────────
335
- with gr.TabItem("πŸ”Š Text β†’ Speech"):
336
  gr.Markdown("""
337
  **Type text** and Voxtral Mini TTS converts it to natural speech.
338
  Optionally paste a **Voice ID** from the Voice Cloning tab to use your own cloned voice.
@@ -389,7 +450,7 @@ with gr.Blocks(title="Voxtral Studio β€” Mistral AI Audio") as demo:
389
  )
390
 
391
  # ── TAB 3: Voice Cloning ───────────────────────────────────────────
392
- with gr.TabItem("🧬 Voice Cloning"):
393
  gr.Markdown("""
394
  **Clone any voice** by uploading a short audio sample (10–60 seconds recommended).
395
  The model will save it as a reusable voice. Copy the Voice ID and paste it in the TTS tab.
 
135
  '-t', '60' # Limit to first 60 seconds to avoid exceeding API limits
136
  ],
137
  }
138
+ try:
139
+ with yt_dlp.YoutubeDL(ydl_opts) as ydl:
140
+ info = ydl.extract_info(url_input.strip(), download=True)
141
+ # after postprocessing, file has .mp3 extension
142
+ final_audio_path = base_out + '.mp3'
143
+ except Exception as e:
144
+ err_msg = str(e)
145
+ if "Sign in to confirm" in err_msg or "bot" in err_msg.lower() or "youtube" in err_msg.lower():
146
+ raise gr.Error("YouTube blocked the Hugging Face datacenter IP. Please try a TikTok/Twitter link instead, or download the MP3 manually and upload it above.")
147
+ else:
148
+ raise gr.Error(f"Video download failed: {err_msg}")
149
 
150
  client = get_client()
151
  sample_b64 = base64.b64encode(Path(final_audio_path).read_bytes()).decode()
 
179
  ]
180
 
181
  css = """
182
+ @import url('https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700;800&display=swap');
183
 
184
+ * { font-family: 'Outfit', sans-serif; }
185
 
186
  body, .gradio-container {
187
+ background: radial-gradient(circle at 10% 20%, #150f24 0%, #07040d 100%) !important;
188
  min-height: 100vh;
189
  }
190
 
191
  .gradio-container {
192
+ max-width: 1050px !important;
193
  margin: 0 auto !important;
194
  }
195
 
196
+ /* App Header */
197
  .app-header {
198
  text-align: center;
199
+ padding: 3.5rem 1rem 1.5rem;
200
+ position: relative;
201
+ z-index: 10;
202
  }
203
  .app-header h1 {
204
+ font-size: 3.8rem;
205
+ font-weight: 800;
206
+ letter-spacing: -1.5px;
207
+ background: linear-gradient(135deg, #c084fc 0%, #ec4899 50%, #facc15 100%);
208
  -webkit-background-clip: text;
209
  -webkit-text-fill-color: transparent;
210
  background-clip: text;
211
+ margin-bottom: 0.5rem;
212
+ animation: glow-pulse 3s infinite alternate;
213
  }
214
  .app-header p {
215
  color: #94a3b8;
216
+ font-size: 1.15rem;
217
+ font-weight: 400;
218
  margin-top: 0;
219
  }
220
 
221
+ /* Glass panel wrapper */
222
+ div.tabs-container, .panel-box {
223
+ background: rgba(255, 255, 255, 0.02) !important;
224
+ border: 1px solid rgba(255, 255, 255, 0.05) !important;
225
+ border-radius: 20px !important;
226
+ box-shadow: 0 10px 40px 0 rgba(0, 0, 0, 0.4) !important;
227
+ backdrop-filter: blur(15px) !important;
228
+ -webkit-backdrop-filter: blur(15px) !important;
229
+ overflow: hidden;
230
+ }
231
+
232
  /* Tabs */
233
+ .tab-nav {
234
+ border-bottom: 1px solid rgba(255,255,255,0.05) !important;
235
+ padding: 10px 10px 0 10px !important;
236
+ }
237
  .tab-nav button {
238
+ background: transparent !important;
239
+ border: none !important;
240
+ border-bottom: 3px solid transparent !important;
241
+ color: #64748b !important;
242
+ border-radius: 0 !important;
243
+ margin: 0 !important;
244
+ padding: 1rem 2rem !important;
245
+ font-weight: 600 !important;
246
+ font-size: 1.05rem !important;
247
+ transition: all 0.3s ease !important;
248
+ box-shadow: none !important;
249
  }
250
  .tab-nav button.selected, .tab-nav button:hover {
251
+ color: #f8fafc !important;
252
+ border-bottom: 3px solid #ec4899 !important;
253
+ box-shadow: 0 20px 20px -20px rgba(236,72,153,0.3) !important;
254
+ background: linear-gradient(0deg, rgba(236,72,153,0.1) 0%, transparent 100%) !important;
255
  }
256
 
257
+ /* Inputs & Textareas */
258
+ textarea, input[type="text"], .dropdown-menu {
259
+ background: rgba(0,0,0,0.25) !important;
260
+ border: 1px solid rgba(255,255,255,0.08) !important;
261
+ border-radius: 14px !important;
262
+ color: #f8fafc !important;
263
+ font-size: 1.05rem !important;
264
+ transition: all 0.2s ease !important;
265
+ padding: 0.75rem !important;
 
 
 
 
 
 
 
 
266
  }
267
  textarea:focus, input[type="text"]:focus {
268
+ border-color: #ec4899 !important;
269
+ box-shadow: 0 0 0 3px rgba(236,72,153,0.2) !important;
270
+ background: rgba(0,0,0,0.4) !important;
271
  }
272
 
273
+ /* Override Gradio layout borders */
274
+ div.form {
275
+ border: none !important;
276
+ box-shadow: none !important;
277
+ background: transparent !important;
278
+ }
279
+
280
+ /* Cool gradient buttons */
281
  button.primary {
282
+ background: linear-gradient(135deg, #a78bfa 0%, #ec4899 100%) !important;
283
  border: none !important;
 
284
  color: white !important;
285
+ border-radius: 14px !important;
286
+ font-weight: 700 !important;
287
+ font-size: 1.15rem !important;
288
+ padding: 0.9rem !important;
289
+ letter-spacing: 0.5px !important;
290
+ box-shadow: 0 4px 15px rgba(236,72,153,0.3) !important;
291
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;
292
  }
293
  button.primary:hover {
294
+ transform: translateY(-3px) !important;
295
+ box-shadow: 0 8px 25px rgba(236,72,153,0.5) !important;
296
+ }
297
+
298
+ /* Secondary Button */
299
+ button.secondary {
300
+ background: rgba(255,255,255,0.05) !important;
301
+ border: 1px solid rgba(255,255,255,0.1) !important;
302
+ border-radius: 14px !important;
303
+ color: #e2e8f0 !important;
304
+ transition: all 0.2s ease !important;
305
+ font-weight: 600 !important;
306
+ }
307
+ button.secondary:hover {
308
+ background: rgba(255,255,255,0.15) !important;
309
+ border-color: rgba(255,255,255,0.3) !important;
310
  }
311
 
312
+ /* Status text box */
313
  .status-text {
314
+ background: rgba(0,0,0,0.4);
315
+ padding: 1.5rem;
316
+ border-radius: 16px;
317
+ border-left: 5px solid #a78bfa;
318
+ color: #e2e8f0;
319
+ font-size: 1rem;
320
+ line-height: 1.6;
321
  }
322
 
323
+ /* Highlight labels */
324
  label span {
325
  color: #cbd5e1 !important;
326
  font-weight: 500 !important;
327
+ letter-spacing: 0.2px !important;
328
  }
329
 
330
+ /* Clean audio components */
331
  .audio-component {
332
+ border-radius: 16px !important;
333
  overflow: hidden !important;
334
+ border: 1px solid rgba(255,255,255,0.05) !important;
335
+ }
336
+
337
+ /* Global Animations */
338
+ @keyframes glow-pulse {
339
+ 0% { filter: drop-shadow(0 0 15px rgba(167,139,250,0.3)); }
340
+ 100% { filter: drop-shadow(0 0 30px rgba(236,72,153,0.6)); }
341
  }
342
+
343
+ /* Footer Hide */
344
+ footer { display: none !important; }
345
  """
346
 
347
  # Initialize voices at startup
348
  INITIAL_VOICES = get_voice_choices()
349
 
350
+ with gr.Blocks(title="Voxtral Studio β€” Mistral AI Audio", css=css) as demo:
351
 
352
  gr.HTML("""
353
  <div class="app-header">
 
393
  )
394
 
395
  # ── TAB 2: Text to Speech ──────────────────────────────────────────
396
+ with gr.TabItem("πŸ”Š Text β†’ Speech", elem_classes=["tabs-container"]):
397
  gr.Markdown("""
398
  **Type text** and Voxtral Mini TTS converts it to natural speech.
399
  Optionally paste a **Voice ID** from the Voice Cloning tab to use your own cloned voice.
 
450
  )
451
 
452
  # ── TAB 3: Voice Cloning ───────────────────────────────────────────
453
+ with gr.TabItem("🧬 Voice Cloning", elem_classes=["tabs-container"]):
454
  gr.Markdown("""
455
  **Clone any voice** by uploading a short audio sample (10–60 seconds recommended).
456
  The model will save it as a reusable voice. Copy the Voice ID and paste it in the TTS tab.