DevNumb commited on
Commit
a90837d
·
verified ·
1 Parent(s): ad4a6e2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +82 -126
app.py CHANGED
@@ -2,12 +2,21 @@ import asyncio
2
  import platform
3
  import sys
4
 
5
- # Fix for "ValueError: Invalid file descriptor: -1" on shutdown
6
- # This is specifically for Hugging Face Spaces environment
7
  if sys.platform.startswith("linux") or sys.platform.startswith("darwin"):
8
  try:
9
- # Use the default policy to prevent faulty cleanup on shutdown
 
 
 
 
 
 
 
 
10
  asyncio.set_event_loop_policy(asyncio.DefaultEventLoopPolicy())
 
 
11
  except Exception:
12
  pass # Ignore any errors if this fails
13
 
@@ -30,6 +39,7 @@ body, .gradio-container {
30
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
31
  margin: 0;
32
  padding: 20px;
 
33
  }
34
 
35
  /* Header */
@@ -44,7 +54,7 @@ body, .gradio-container {
44
  }
45
 
46
  .header h1 {
47
- font-size: 2.8em;
48
  margin: 0 0 0.5rem 0;
49
  font-weight: 700;
50
  letter-spacing: -0.5px;
@@ -61,9 +71,9 @@ body, .gradio-container {
61
  background: white;
62
  border: 1px solid #E5E7EB;
63
  border-radius: 16px;
64
- padding: 1.75rem;
65
  margin-bottom: 1.5rem;
66
- box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
67
  }
68
 
69
  /* Text Input - BLACK TEXT ON WHITE */
@@ -71,12 +81,12 @@ textarea {
71
  background: white !important;
72
  border: 2px solid #D1D5DB !important;
73
  border-radius: 12px !important;
74
- color: #111827 !important;
75
  padding: 1rem !important;
76
  font-size: 16px !important;
77
  font-family: 'SF Mono', Monaco, 'Courier New', monospace !important;
78
  width: 100% !important;
79
- min-height: 140px !important;
80
  line-height: 1.5 !important;
81
  }
82
 
@@ -84,11 +94,11 @@ textarea:focus {
84
  border-color: #4F46E5 !important;
85
  box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1) !important;
86
  outline: none !important;
87
- color: #111827 !important;
88
  }
89
 
90
  textarea::placeholder {
91
- color: #6B7280 !important;
92
  opacity: 0.8 !important;
93
  }
94
 
@@ -97,16 +107,17 @@ textarea::placeholder {
97
  background: linear-gradient(135deg, #4F46E5 0%, #7C3AED 100%) !important;
98
  border: none !important;
99
  color: white !important;
100
- padding: 0.875rem 1.75rem !important;
101
- border-radius: 12px !important;
102
  font-weight: 600 !important;
103
  font-size: 1rem !important;
104
  cursor: pointer !important;
 
105
  }
106
 
107
  .btn-primary:hover {
108
- transform: translateY(-1px) !important;
109
- box-shadow: 0 4px 15px rgba(79, 70, 229, 0.3) !important;
110
  }
111
 
112
  .btn-secondary {
@@ -114,9 +125,10 @@ textarea::placeholder {
114
  border: 2px solid #D1D5DB !important;
115
  color: #374151 !important;
116
  padding: 0.75rem 1.5rem !important;
117
- border-radius: 12px !important;
118
  font-weight: 500 !important;
119
  cursor: pointer !important;
 
120
  }
121
 
122
  .btn-secondary:hover {
@@ -126,40 +138,29 @@ textarea::placeholder {
126
  }
127
 
128
  /* Slider */
129
- .slider-container {
130
- padding: 1rem 0;
131
- }
132
-
133
- .slider-container label {
134
- display: block;
135
- color: #374151 !important;
136
- font-weight: 600 !important;
137
- margin-bottom: 0.5rem !important;
138
- font-size: 0.95rem !important;
139
- }
140
-
141
  input[type="range"] {
142
  width: 100% !important;
143
  height: 6px !important;
144
  background: #E5E7EB !important;
145
  border-radius: 10px !important;
146
  outline: none !important;
 
147
  }
148
 
149
  input[type="range"]::-webkit-slider-thumb {
150
- width: 22px !important;
151
- height: 22px !important;
152
  background: #4F46E5 !important;
153
  border: 3px solid white !important;
154
  border-radius: 50% !important;
155
- box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2) !important;
156
  cursor: pointer !important;
157
  }
158
 
159
  /* Audio Player */
160
  .audio-player {
161
  background: #F9FAFB !important;
162
- border-radius: 16px !important;
163
  padding: 1.5rem !important;
164
  margin-top: 1rem !important;
165
  border: 1px solid #E5E7EB !important;
@@ -182,22 +183,20 @@ input[type="range"]::-webkit-slider-thumb {
182
  background: white;
183
  border: 1px solid #E5E7EB;
184
  border-radius: 12px;
185
- padding: 1.25rem;
186
  text-align: center;
187
  }
188
 
189
  .stat-value {
190
- font-size: 2em;
191
  font-weight: 700;
192
- background: linear-gradient(135deg, #4F46E5 0%, #7C3AED 100%);
193
- -webkit-background-clip: text;
194
- -webkit-text-fill-color: transparent;
195
  margin-bottom: 0.25rem;
196
  }
197
 
198
  .stat-label {
199
  color: #6B7280;
200
- font-size: 0.85em;
201
  text-transform: uppercase;
202
  letter-spacing: 0.5px;
203
  font-weight: 600;
@@ -210,7 +209,7 @@ input[type="range"]::-webkit-slider-thumb {
210
  border-left: 4px solid #10B981 !important;
211
  color: #065F46 !important;
212
  padding: 1rem !important;
213
- border-radius: 10px !important;
214
  margin: 1rem 0 !important;
215
  }
216
 
@@ -220,7 +219,7 @@ input[type="range"]::-webkit-slider-thumb {
220
  border-left: 4px solid #EF4444 !important;
221
  color: #991B1B !important;
222
  padding: 1rem !important;
223
- border-radius: 10px !important;
224
  margin: 1rem 0 !important;
225
  }
226
 
@@ -228,14 +227,14 @@ input[type="range"]::-webkit-slider-thumb {
228
  .footer {
229
  text-align: center;
230
  padding: 2rem;
231
- margin-top: 3rem;
232
  color: #6B7280;
233
  border-top: 1px solid #E5E7EB;
234
  }
235
 
236
  /* Grid layout */
237
  .container {
238
- max-width: 1200px;
239
  margin: 0 auto;
240
  }
241
 
@@ -262,13 +261,10 @@ input[type="range"]::-webkit-slider-thumb {
262
  .markdown h1, .markdown h2, .markdown h3 {
263
  color: #111827 !important;
264
  font-weight: 600 !important;
265
- margin-top: 1.5em !important;
266
- margin-bottom: 0.5em !important;
267
  }
268
 
269
  .markdown p {
270
  color: #4B5563 !important;
271
- margin-bottom: 1em !important;
272
  }
273
  </style>
274
  """
@@ -361,8 +357,7 @@ def generate_tts(text, speed=1.0, emotion="neutral"):
361
  <div style="color: #065F46;">
362
  <strong>{len(text)} characters</strong> •
363
  <strong>{duration:.1f}s duration</strong> •
364
- Speed: <strong>{speed}x</strong>
365
- Emotion: <strong>{emotion}</strong>
366
  </div>
367
  </div>
368
  """
@@ -420,8 +415,8 @@ with gr.Blocks() as demo:
420
  # Header
421
  gr.HTML("""
422
  <div class="header">
423
- <h1>🎵 VibeVoice Text-to-Speech</h1>
424
- <p>Convert text into natural-sounding speech</p>
425
  </div>
426
  """)
427
 
@@ -434,17 +429,16 @@ with gr.Blocks() as demo:
434
 
435
  text_input = gr.Textbox(
436
  label="",
437
- placeholder="Type or paste your text here...",
438
- lines=6,
439
- elem_id="text-input"
440
  )
441
 
442
- gr.Markdown("### ⚙️ Voice Settings")
443
 
444
  with gr.Row():
445
  emotion = gr.Dropdown(
446
  label="Voice Style",
447
- choices=["Neutral", "Happy", "Calm", "Professional", "Storytelling"],
448
  value="Neutral"
449
  )
450
 
@@ -453,8 +447,7 @@ with gr.Blocks() as demo:
453
  maximum=2.0,
454
  value=1.0,
455
  step=0.1,
456
- label="Speaking Speed",
457
- elem_classes="slider-container"
458
  )
459
 
460
  with gr.Row():
@@ -464,7 +457,7 @@ with gr.Blocks() as demo:
464
  elem_classes="btn-primary"
465
  )
466
  clear_btn = gr.Button(
467
- "Clear All",
468
  variant="secondary",
469
  elem_classes="btn-secondary"
470
  )
@@ -477,13 +470,12 @@ with gr.Blocks() as demo:
477
  with gr.Column(elem_classes="audio-player"):
478
  audio_output = gr.Audio(
479
  type="filepath",
480
- label="",
481
- elem_id="audio-output"
482
  )
483
 
484
  status_display = gr.HTML(
485
  """<div style="text-align: center; color: #6B7280; padding: 1rem;">
486
- Ready to generate speech. Enter text and click the button above.
487
  </div>"""
488
  )
489
 
@@ -491,60 +483,36 @@ with gr.Blocks() as demo:
491
  with gr.Column(elem_classes="card"):
492
  gr.Markdown("### 📊 Statistics")
493
  stats_display = gr.HTML(get_stats_html())
494
-
495
- with gr.Row():
496
- refresh_stats = gr.Button(
497
- "🔄 Refresh Stats",
498
- variant="secondary",
499
- elem_classes="btn-secondary"
500
- )
501
 
502
  # Examples
503
  with gr.Column(elem_classes="card"):
504
- gr.Markdown("### 💡 Example Texts")
505
 
506
  gr.Examples(
507
  examples=[
508
- ["Hello! Welcome to VibeVoice text-to-speech system."],
509
  ["The quick brown fox jumps over the lazy dog."],
510
- ["Artificial intelligence is revolutionizing technology."],
511
- ["Would you like a cup of coffee or tea this morning?"],
512
- ["This text-to-speech system converts written text into spoken words."]
513
  ],
514
  inputs=text_input,
515
- label="Click any example to load it:",
516
- examples_per_page=5
517
  )
518
 
519
  # About section
520
  with gr.Column(elem_classes="card"):
521
- gr.Markdown("### ℹ️ About VibeVoice TTS")
522
  gr.Markdown("""
523
- **VibeVoice TTS** converts written text into natural-sounding speech.
524
 
525
  **Features:**
526
- - 🎵 High-quality audio output
527
- - ⚡ Fast processing speed
528
- - 🎭 Multiple voice styles
529
- - ⚙️ Adjustable speaking rate
530
 
531
- **Tips:**
532
- 1. Use clear, well-punctuated text
533
- 2. Keep text under 500 characters
534
- 3. Adjust speed to match your preference
535
  """)
536
-
537
- # Footer
538
- gr.HTML("""
539
- <div class="footer">
540
- <div style="margin-bottom: 1rem;">
541
- <span style="color: #4F46E5; font-weight: 600;">VibeVoice TTS</span>
542
- </div>
543
- <div style="font-size: 0.9em; color: #9CA3AF;">
544
- Made with Gradio
545
- </div>
546
- </div>
547
- """)
548
 
549
  # Event handlers
550
  def process_text(text, speed_val, emotion_val):
@@ -553,7 +521,6 @@ with gr.Blocks() as demo:
553
  return None, """
554
  <div class="status-error">
555
  <div style="font-weight: 600;">⚠️ Please enter text</div>
556
- <div>Type or paste some text in the input box above.</div>
557
  </div>
558
  """, get_stats_html()
559
 
@@ -562,8 +529,8 @@ with gr.Blocks() as demo:
562
  def clear_all():
563
  """Clear all inputs"""
564
  return "", None, """
565
- <div style="text-align: center; padding: 1rem; color: #6B7280; border: 1px dashed #D1D5DB; border-radius: 10px;">
566
- Input cleared. Ready for new text.
567
  </div>
568
  """, get_stats_html()
569
 
@@ -579,34 +546,23 @@ with gr.Blocks() as demo:
579
  inputs=[],
580
  outputs=[text_input, audio_output, status_display, stats_display]
581
  )
582
-
583
- refresh_stats.click(
584
- fn=get_stats_html,
585
- inputs=[],
586
- outputs=[stats_display]
587
- )
588
-
589
- # Initialize stats on load
590
- demo.load(
591
- fn=get_stats_html,
592
- inputs=[],
593
- outputs=[stats_display]
594
- )
595
 
596
- # Launch the app with Hugging Face Space-specific fix
597
  if __name__ == "__main__":
 
598
  try:
599
- demo.launch(
600
- server_name="0.0.0.0",
601
- server_port=7860,
602
- show_error=True,
603
- quiet=True,
604
- debug=False,
605
- # CRITICAL FIX for Hugging Face Spaces
606
- # Prevents the "ValueError: Invalid file descriptor: -1" error
607
- close_event_loop=False
608
- )
609
- except KeyboardInterrupt:
610
- print("\n👋 Shutting down VibeVoice TTS...")
611
- except Exception as e:
612
- print(f"❌ Error: {e}")
 
 
2
  import platform
3
  import sys
4
 
5
+ # Fix for asyncio warnings on Hugging Face Spaces
 
6
  if sys.platform.startswith("linux") or sys.platform.startswith("darwin"):
7
  try:
8
+ # Clean up any existing loops
9
+ try:
10
+ loop = asyncio.get_event_loop()
11
+ if loop.is_running():
12
+ loop.close()
13
+ except:
14
+ pass
15
+
16
+ # Set default policy
17
  asyncio.set_event_loop_policy(asyncio.DefaultEventLoopPolicy())
18
+ # Create a new event loop
19
+ asyncio.set_event_loop(asyncio.new_event_loop())
20
  except Exception:
21
  pass # Ignore any errors if this fails
22
 
 
39
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
40
  margin: 0;
41
  padding: 20px;
42
+ min-height: 100vh;
43
  }
44
 
45
  /* Header */
 
54
  }
55
 
56
  .header h1 {
57
+ font-size: 2.5em;
58
  margin: 0 0 0.5rem 0;
59
  font-weight: 700;
60
  letter-spacing: -0.5px;
 
71
  background: white;
72
  border: 1px solid #E5E7EB;
73
  border-radius: 16px;
74
+ padding: 1.5rem;
75
  margin-bottom: 1.5rem;
76
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
77
  }
78
 
79
  /* Text Input - BLACK TEXT ON WHITE */
 
81
  background: white !important;
82
  border: 2px solid #D1D5DB !important;
83
  border-radius: 12px !important;
84
+ color: #000000 !important; /* Pure black text */
85
  padding: 1rem !important;
86
  font-size: 16px !important;
87
  font-family: 'SF Mono', Monaco, 'Courier New', monospace !important;
88
  width: 100% !important;
89
+ min-height: 120px !important;
90
  line-height: 1.5 !important;
91
  }
92
 
 
94
  border-color: #4F46E5 !important;
95
  box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1) !important;
96
  outline: none !important;
97
+ color: #000000 !important;
98
  }
99
 
100
  textarea::placeholder {
101
+ color: #666666 !important;
102
  opacity: 0.8 !important;
103
  }
104
 
 
107
  background: linear-gradient(135deg, #4F46E5 0%, #7C3AED 100%) !important;
108
  border: none !important;
109
  color: white !important;
110
+ padding: 0.75rem 1.5rem !important;
111
+ border-radius: 10px !important;
112
  font-weight: 600 !important;
113
  font-size: 1rem !important;
114
  cursor: pointer !important;
115
+ margin: 0.5rem !important;
116
  }
117
 
118
  .btn-primary:hover {
119
+ transform: translateY(-2px) !important;
120
+ box-shadow: 0 4px 12px rgba(79, 70, 229, 0.3) !important;
121
  }
122
 
123
  .btn-secondary {
 
125
  border: 2px solid #D1D5DB !important;
126
  color: #374151 !important;
127
  padding: 0.75rem 1.5rem !important;
128
+ border-radius: 10px !important;
129
  font-weight: 500 !important;
130
  cursor: pointer !important;
131
+ margin: 0.5rem !important;
132
  }
133
 
134
  .btn-secondary:hover {
 
138
  }
139
 
140
  /* Slider */
 
 
 
 
 
 
 
 
 
 
 
 
141
  input[type="range"] {
142
  width: 100% !important;
143
  height: 6px !important;
144
  background: #E5E7EB !important;
145
  border-radius: 10px !important;
146
  outline: none !important;
147
+ margin: 1rem 0 !important;
148
  }
149
 
150
  input[type="range"]::-webkit-slider-thumb {
151
+ width: 20px !important;
152
+ height: 20px !important;
153
  background: #4F46E5 !important;
154
  border: 3px solid white !important;
155
  border-radius: 50% !important;
156
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2) !important;
157
  cursor: pointer !important;
158
  }
159
 
160
  /* Audio Player */
161
  .audio-player {
162
  background: #F9FAFB !important;
163
+ border-radius: 12px !important;
164
  padding: 1.5rem !important;
165
  margin-top: 1rem !important;
166
  border: 1px solid #E5E7EB !important;
 
183
  background: white;
184
  border: 1px solid #E5E7EB;
185
  border-radius: 12px;
186
+ padding: 1rem;
187
  text-align: center;
188
  }
189
 
190
  .stat-value {
191
+ font-size: 1.8em;
192
  font-weight: 700;
193
+ color: #4F46E5 !important;
 
 
194
  margin-bottom: 0.25rem;
195
  }
196
 
197
  .stat-label {
198
  color: #6B7280;
199
+ font-size: 0.8em;
200
  text-transform: uppercase;
201
  letter-spacing: 0.5px;
202
  font-weight: 600;
 
209
  border-left: 4px solid #10B981 !important;
210
  color: #065F46 !important;
211
  padding: 1rem !important;
212
+ border-radius: 8px !important;
213
  margin: 1rem 0 !important;
214
  }
215
 
 
219
  border-left: 4px solid #EF4444 !important;
220
  color: #991B1B !important;
221
  padding: 1rem !important;
222
+ border-radius: 8px !important;
223
  margin: 1rem 0 !important;
224
  }
225
 
 
227
  .footer {
228
  text-align: center;
229
  padding: 2rem;
230
+ margin-top: 2rem;
231
  color: #6B7280;
232
  border-top: 1px solid #E5E7EB;
233
  }
234
 
235
  /* Grid layout */
236
  .container {
237
+ max-width: 1000px;
238
  margin: 0 auto;
239
  }
240
 
 
261
  .markdown h1, .markdown h2, .markdown h3 {
262
  color: #111827 !important;
263
  font-weight: 600 !important;
 
 
264
  }
265
 
266
  .markdown p {
267
  color: #4B5563 !important;
 
268
  }
269
  </style>
270
  """
 
357
  <div style="color: #065F46;">
358
  <strong>{len(text)} characters</strong> •
359
  <strong>{duration:.1f}s duration</strong> •
360
+ Speed: <strong>{speed}x</strong>
 
361
  </div>
362
  </div>
363
  """
 
415
  # Header
416
  gr.HTML("""
417
  <div class="header">
418
+ <h1>🎵 VibeVoice TTS</h1>
419
+ <p>Text-to-Speech with Clean White Interface</p>
420
  </div>
421
  """)
422
 
 
429
 
430
  text_input = gr.Textbox(
431
  label="",
432
+ placeholder="Type or paste your text here... (Black text on white background)",
433
+ lines=5
 
434
  )
435
 
436
+ gr.Markdown("### ⚙️ Settings")
437
 
438
  with gr.Row():
439
  emotion = gr.Dropdown(
440
  label="Voice Style",
441
+ choices=["Neutral", "Happy", "Calm"],
442
  value="Neutral"
443
  )
444
 
 
447
  maximum=2.0,
448
  value=1.0,
449
  step=0.1,
450
+ label="Speaking Speed"
 
451
  )
452
 
453
  with gr.Row():
 
457
  elem_classes="btn-primary"
458
  )
459
  clear_btn = gr.Button(
460
+ "Clear",
461
  variant="secondary",
462
  elem_classes="btn-secondary"
463
  )
 
470
  with gr.Column(elem_classes="audio-player"):
471
  audio_output = gr.Audio(
472
  type="filepath",
473
+ label=""
 
474
  )
475
 
476
  status_display = gr.HTML(
477
  """<div style="text-align: center; color: #6B7280; padding: 1rem;">
478
+ Ready. Enter text and click Generate.
479
  </div>"""
480
  )
481
 
 
483
  with gr.Column(elem_classes="card"):
484
  gr.Markdown("### 📊 Statistics")
485
  stats_display = gr.HTML(get_stats_html())
 
 
 
 
 
 
 
486
 
487
  # Examples
488
  with gr.Column(elem_classes="card"):
489
+ gr.Markdown("### 💡 Examples")
490
 
491
  gr.Examples(
492
  examples=[
493
+ ["Hello! Welcome to the text-to-speech system."],
494
  ["The quick brown fox jumps over the lazy dog."],
495
+ ["This is a test of the audio generation."],
496
+ ["The weather is beautiful today."]
 
497
  ],
498
  inputs=text_input,
499
+ label="Click to try:"
 
500
  )
501
 
502
  # About section
503
  with gr.Column(elem_classes="card"):
504
+ gr.Markdown("### ℹ️ About")
505
  gr.Markdown("""
506
+ **VibeVoice TTS** converts text to audio.
507
 
508
  **Features:**
509
+ - 🎵 Audio generation
510
+ - ⚡ Fast processing
511
+ - 🎭 Voice styles
512
+ - ⚙️ Speed control
513
 
514
+ **Note:** Text input shows **black text on white background**.
 
 
 
515
  """)
 
 
 
 
 
 
 
 
 
 
 
 
516
 
517
  # Event handlers
518
  def process_text(text, speed_val, emotion_val):
 
521
  return None, """
522
  <div class="status-error">
523
  <div style="font-weight: 600;">⚠️ Please enter text</div>
 
524
  </div>
525
  """, get_stats_html()
526
 
 
529
  def clear_all():
530
  """Clear all inputs"""
531
  return "", None, """
532
+ <div style="text-align: center; color: #6B7280; padding: 1rem;">
533
+ Cleared. Ready for new text.
534
  </div>
535
  """, get_stats_html()
536
 
 
546
  inputs=[],
547
  outputs=[text_input, audio_output, status_display, stats_display]
548
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
549
 
550
+ # Launch the app - SIMPLIFIED for Gradio 3.x
551
  if __name__ == "__main__":
552
+ # Clean up any existing event loops before starting
553
  try:
554
+ import asyncio
555
+ loop = asyncio.get_event_loop()
556
+ if loop.is_running():
557
+ loop.close()
558
+ except:
559
+ pass
560
+
561
+ # Launch with minimal parameters
562
+ demo.launch(
563
+ server_name="0.0.0.0",
564
+ server_port=7860,
565
+ show_error=True,
566
+ quiet=True,
567
+ debug=False
568
+ )