YanTianlong commited on
Commit
111c74c
·
1 Parent(s): 52eb4fd

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +225 -71
app.py CHANGED
@@ -81,44 +81,168 @@ TARGET_LANGUAGES = [
81
  "Vietnamese",
82
  ]
83
  APP_CSS = """
84
- .voicegate-hero {
85
- border: 1px solid #e5e7eb;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
  border-radius: 8px;
87
- padding: 22px 24px;
88
- background: #f8fafc;
 
 
 
 
 
89
  }
90
- .voicegate-hero h1 {
91
- margin: 0 0 8px;
 
 
 
 
 
 
 
 
92
  font-size: 30px;
93
- line-height: 1.15;
 
94
  }
95
- .voicegate-hero p {
 
96
  margin: 0;
97
- color: #475569;
98
- font-size: 15px;
 
99
  }
100
- .voicegate-links a {
101
- display: inline-block;
102
- margin: 4px 10px 4px 0;
 
 
103
  }
104
- .voicegate-section {
105
- border: 1px solid #e5e7eb;
106
- border-radius: 8px;
107
- padding: 14px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
  background: #ffffff;
 
 
 
 
 
 
 
 
 
 
 
109
  }
110
- .voicegate-section h3 {
111
- margin: 0 0 8px;
112
- font-size: 16px;
113
  }
114
- .voicegate-note {
115
- color: #475569;
 
 
 
116
  font-size: 13px;
117
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
  .voicegate-status textarea {
119
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
120
  font-size: 12px;
121
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
  """
123
 
124
 
@@ -134,6 +258,49 @@ def gpu_status_lines() -> list[str]:
134
  return lines
135
 
136
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
  def wait_for_comfy(timeout: float = 180) -> dict[str, Any]:
138
  deadline = time.time() + timeout
139
  last_error = ""
@@ -837,46 +1004,31 @@ def voicegate_user_run(audio_path: str | None, target_language: str, tts_trim_st
837
  return None, "\n".join(lines), None, None, "", ""
838
 
839
 
840
- with gr.Blocks(title="VoiceGate") as demo:
841
- gr.HTML(
842
- """
843
- <section class="voicegate-hero">
844
- <h1>VoiceGate</h1>
845
- <p>Cross-language dubbing for audio and video. VoiceGate combines ASR, LLM translation, multilingual TTS, subtitle timing alignment, and vocal/background separation to generate translated speech while preserving the original ambience.</p>
846
- </section>
847
- """
848
- )
849
  with gr.Tab("Translate"):
850
- gr.Markdown(
851
- """
852
- VoiceGate is built with VoxCPM2 and ComfyUI. It supports multilingual dubbing for content localization, video translation, education, narration, and creator workflows. The pipeline extracts timed subtitles, translates them, generates target-language speech, aligns each speech segment back to the subtitle timeline, and mixes it with the original background audio.
853
- """
854
- )
855
  gr.HTML(
856
  """
857
- <div class="voicegate-links">
858
- <a href="https://www.runninghub.ai/ai-detail/2062442306350964737?inviteCode=rh-v1455" target="_blank">Online app - audio</a>
859
- <a href="https://www.runninghub.ai/ai-detail/2062446982618238978?inviteCode=rh-v1455" target="_blank">Online app - video</a>
860
- <a href="https://www.runninghub.ai/post/2062432233125928961?inviteCode=rh-v1455" target="_blank">ComfyUI workflow - audio</a>
861
- <a href="https://www.runninghub.ai/post/2062445363042283522?inviteCode=rh-v1455" target="_blank">ComfyUI workflow - video</a>
862
- </div>
863
- """
864
- )
865
- gr.Markdown(
866
- """
867
- **How to use**
868
-
869
- 1. Upload a short audio clip.
870
- 2. Choose the target language.
871
- 3. Generate the translated dubbing and review the original and translated subtitles.
872
-
873
- The estimated processing time is roughly equal to the duration of the uploaded audio once the Space is warm. The first run after a rebuild can take longer while the environment and models are prepared.
874
  """
875
  )
876
- with gr.Row():
877
- with gr.Column(scale=5):
878
- with gr.Group(elem_classes=["voicegate-section"]):
879
- gr.Markdown("### Input")
880
  user_audio = gr.Audio(label="Upload audio", type="filepath")
881
  user_target_language = gr.Dropdown(
882
  label="Target language",
@@ -895,20 +1047,22 @@ with gr.Blocks(title="VoiceGate") as demo:
895
  "Use this to remove short noises that may appear at the beginning of generated speech segments."
896
  ),
897
  )
898
- user_run = gr.Button("Generate translated dubbing", variant="primary")
899
- with gr.Column(scale=6):
900
- with gr.Group(elem_classes=["voicegate-section"]):
901
- gr.Markdown("### Output")
 
902
  user_output_audio = gr.Audio(label="Translated dubbing audio", type="filepath")
903
- with gr.Row():
904
- user_source_file = gr.File(label="Original subtitle file")
905
- user_translated_file = gr.File(label="Translated subtitle file")
906
- with gr.Row():
907
- user_source_text = gr.Textbox(label="Original subtitles", lines=8)
908
- user_translated_text = gr.Textbox(label="Translated subtitles", lines=8)
909
- with gr.Group(elem_classes=["voicegate-section"]):
910
- gr.Markdown("### Log")
911
- user_status = gr.Textbox(label="Status", lines=14, elem_classes=["voicegate-status"])
 
912
  user_run.click(
913
  fn=voicegate_user_run,
914
  inputs=[user_audio, user_target_language, user_tts_trim_start],
@@ -961,4 +1115,4 @@ with gr.Blocks(title="VoiceGate") as demo:
961
 
962
 
963
  if __name__ == "__main__":
964
- demo.launch(css=APP_CSS)
 
81
  "Vietnamese",
82
  ]
83
  APP_CSS = """
84
+ :root {
85
+ --vg-primary: #6366c7;
86
+ --vg-primary-dark: #5255b5;
87
+ --vg-ink: #171827;
88
+ --vg-muted: #667085;
89
+ --vg-line: #eceef5;
90
+ --vg-soft: #f6f7fb;
91
+ }
92
+ :root:root:root:root main {
93
+ max-width: 1160px;
94
+ }
95
+ :root:root:root:root .gradio-container {
96
+ overflow: unset;
97
+ }
98
+ .voicegate-shell {
99
+ gap: 16px;
100
+ }
101
+ .voicegate-card {
102
+ background: #ffffff;
103
+ border: 1px solid var(--vg-line);
104
  border-radius: 8px;
105
+ padding: 12px;
106
+ }
107
+ .voicegate-intro {
108
+ margin: 10px 0 12px;
109
+ padding: 18px;
110
+ border-color: rgba(99, 102, 199, 0.24);
111
+ background: linear-gradient(180deg, #ffffff 0%, #f8f8ff 100%);
112
  }
113
+ .voicegate-kicker {
114
+ color: var(--vg-primary);
115
+ font-size: 12px;
116
+ font-weight: 700;
117
+ letter-spacing: 0;
118
+ text-transform: uppercase;
119
+ }
120
+ .voicegate-intro h1 {
121
+ margin: 6px 0 8px;
122
+ color: var(--vg-ink);
123
  font-size: 30px;
124
+ line-height: 1.12;
125
+ letter-spacing: 0;
126
  }
127
+ .voicegate-intro p {
128
+ max-width: 760px;
129
  margin: 0;
130
+ color: var(--vg-muted);
131
+ font-size: 14px;
132
+ line-height: 1.6;
133
  }
134
+ .voicegate-link-row {
135
+ display: flex;
136
+ flex-wrap: wrap;
137
+ gap: 8px;
138
+ margin-top: 14px;
139
  }
140
+ .voicegate-link-row a {
141
+ display: inline-flex;
142
+ min-height: 34px;
143
+ align-items: center;
144
+ justify-content: center;
145
+ border: 1px solid rgba(99, 102, 199, 0.34);
146
+ border-radius: 6px;
147
+ padding: 6px 12px;
148
+ color: var(--vg-primary) !important;
149
+ background: #ffffff;
150
+ font-size: 13px;
151
+ font-weight: 650;
152
+ text-decoration: none;
153
+ }
154
+ .voicegate-link-row a:hover {
155
+ border-color: var(--vg-primary);
156
+ background: #f4f4ff;
157
+ }
158
+ .voicegate-card-label {
159
+ display: inline-flex;
160
+ align-items: center;
161
+ margin: 0 0 10px;
162
+ border-radius: 6px;
163
+ padding: 5px 8px;
164
+ background: #ececf1;
165
+ color: var(--vg-ink);
166
+ font-size: 12px;
167
+ font-weight: 700;
168
+ letter-spacing: 0;
169
+ text-transform: uppercase;
170
+ }
171
+ .voicegate-card-label .voicegate-tag {
172
+ margin-left: 8px;
173
+ border-radius: 999px;
174
+ padding: 2px 7px;
175
+ color: var(--vg-primary);
176
  background: #ffffff;
177
+ font-size: 12px;
178
+ font-weight: 700;
179
+ text-transform: none;
180
+ }
181
+ .voicegate-card .block,
182
+ .voicegate-card .form,
183
+ .voicegate-card textarea,
184
+ .voicegate-card input {
185
+ border-color: var(--vg-line) !important;
186
+ border-width: 1px !important;
187
+ box-shadow: none !important;
188
  }
189
+ .voicegate-card .block {
190
+ border: 1px solid var(--vg-line) !important;
 
191
  }
192
+ .voicegate-card .block:hover,
193
+ .voicegate-card .block:focus-within {
194
+ border-color: rgba(99, 102, 199, 0.34) !important;
195
+ }
196
+ .voicegate-card textarea {
197
  font-size: 13px;
198
  }
199
+ .voicegate-run-card button.primary,
200
+ .voicegate-run-card .primary {
201
+ width: 100%;
202
+ background: var(--vg-primary) !important;
203
+ border-color: var(--vg-primary) !important;
204
+ color: #ffffff !important;
205
+ }
206
+ .voicegate-run-card button.primary:hover,
207
+ .voicegate-run-card .primary:hover {
208
+ background: var(--vg-primary-dark) !important;
209
+ border-color: var(--vg-primary-dark) !important;
210
+ }
211
+ .voicegate-downloads {
212
+ gap: 10px;
213
+ }
214
+ .voicegate-downloads button,
215
+ .voicegate-downloads a {
216
+ width: 100%;
217
+ }
218
  .voicegate-status textarea {
219
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
220
  font-size: 12px;
221
  }
222
+ :root:root:root:root input[type="range"] {
223
+ accent-color: var(--vg-primary);
224
+ }
225
+ :root:root:root:root input[type="range"]::-moz-range-thumb,
226
+ :root:root:root:root input[type="range"]::-webkit-slider-thumb {
227
+ background: var(--vg-primary);
228
+ box-shadow: none;
229
+ }
230
+ :root:root:root:root .tab-container button.selected,
231
+ :root:root:root:root button[role="tab"][aria-selected="true"] {
232
+ color: var(--vg-primary);
233
+ border-color: var(--vg-primary);
234
+ }
235
+ :root:root:root:root footer {
236
+ display: none;
237
+ }
238
+ @media (max-width: 760px) {
239
+ .voicegate-intro h1 {
240
+ font-size: 26px;
241
+ }
242
+ .voicegate-link-row a {
243
+ flex: 1 1 46%;
244
+ }
245
+ }
246
  """
247
 
248
 
 
258
  return lines
259
 
260
 
261
+ def voicegate_theme() -> gr.Theme:
262
+ primary = gr.themes.Color(
263
+ name="voicegate",
264
+ c50="#f5f5ff",
265
+ c100="#ececff",
266
+ c200="#dadaff",
267
+ c300="#b8b9fb",
268
+ c400="#9193ee",
269
+ c500="#6366c7",
270
+ c600="#5255b5",
271
+ c700="#444695",
272
+ c800="#393b78",
273
+ c900="#313262",
274
+ c950="#1f2040",
275
+ )
276
+ return gr.themes.Base(
277
+ primary_hue=primary,
278
+ secondary_hue=gr.themes.colors.neutral,
279
+ radius_size=gr.themes.sizes.radius_sm,
280
+ font=[gr.themes.GoogleFont("Open Sans"), "ui-sans-serif", "system-ui", "sans-serif"],
281
+ ).set(
282
+ background_fill_primary="*neutral_100",
283
+ background_fill_secondary="*neutral_50",
284
+ block_background_fill="white",
285
+ block_border_width="0",
286
+ block_label_background_fill="*neutral_100",
287
+ block_label_border_width="none",
288
+ block_label_margin="0.5rem",
289
+ block_label_radius="*radius_sm",
290
+ block_label_text_color="*neutral_700",
291
+ block_label_text_size="*text_sm",
292
+ block_label_text_weight="600",
293
+ block_padding="0.5rem",
294
+ border_color_primary="transparent",
295
+ button_primary_background_fill="*primary_500",
296
+ button_primary_background_fill_hover="*primary_600",
297
+ button_primary_text_color="white",
298
+ input_background_fill="*neutral_50",
299
+ shadow_drop="none",
300
+ slider_color="*primary_500",
301
+ )
302
+
303
+
304
  def wait_for_comfy(timeout: float = 180) -> dict[str, Any]:
305
  deadline = time.time() + timeout
306
  last_error = ""
 
1004
  return None, "\n".join(lines), None, None, "", ""
1005
 
1006
 
1007
+ with gr.Blocks(title="VoiceGate", fill_width=True) as demo:
 
 
 
 
 
 
 
 
1008
  with gr.Tab("Translate"):
 
 
 
 
 
1009
  gr.HTML(
1010
  """
1011
+ <section class="voicegate-card voicegate-intro">
1012
+ <div class="voicegate-kicker">ComfyUI workflow · multilingual dubbing</div>
1013
+ <h1>VoiceGate</h1>
1014
+ <p>
1015
+ VoiceGate turns speech clips into translated dubbing with timed subtitles and preserved ambience.
1016
+ The warm-run time is usually close to the uploaded audio duration. First runs after rebuilds can
1017
+ take longer while models are prepared.
1018
+ </p>
1019
+ <div class="voicegate-link-row">
1020
+ <a href="https://www.runninghub.ai/ai-detail/2062442306350964737?inviteCode=rh-v1455" target="_blank">Online app - audio</a>
1021
+ <a href="https://www.runninghub.ai/ai-detail/2062446982618238978?inviteCode=rh-v1455" target="_blank">Online app - video</a>
1022
+ <a href="https://www.runninghub.ai/post/2062432233125928961?inviteCode=rh-v1455" target="_blank">ComfyUI workflow - audio</a>
1023
+ <a href="https://www.runninghub.ai/post/2062445363042283522?inviteCode=rh-v1455" target="_blank">ComfyUI workflow - video</a>
1024
+ </div>
1025
+ </section>
 
 
1026
  """
1027
  )
1028
+ with gr.Row(elem_classes=["voicegate-shell"]):
1029
+ with gr.Column(scale=4, min_width=300):
1030
+ with gr.Group(elem_classes=["voicegate-card"]):
1031
+ gr.HTML('<div class="voicegate-card-label">Input <span class="voicegate-tag">required</span></div>')
1032
  user_audio = gr.Audio(label="Upload audio", type="filepath")
1033
  user_target_language = gr.Dropdown(
1034
  label="Target language",
 
1047
  "Use this to remove short noises that may appear at the beginning of generated speech segments."
1048
  ),
1049
  )
1050
+ with gr.Group(elem_classes=["voicegate-run-card"]):
1051
+ user_run = gr.Button("Generate translated dubbing", variant="primary")
1052
+ with gr.Column(scale=8, min_width=420):
1053
+ with gr.Group(elem_classes=["voicegate-card"]):
1054
+ gr.HTML('<div class="voicegate-card-label">Output <span class="voicegate-tag">audio + subtitles</span></div>')
1055
  user_output_audio = gr.Audio(label="Translated dubbing audio", type="filepath")
1056
+ with gr.Row(elem_classes=["voicegate-downloads"]):
1057
+ user_source_file = gr.DownloadButton("Download original subtitles", size="sm")
1058
+ user_translated_file = gr.DownloadButton("Download translated subtitles", size="sm")
1059
+ with gr.Accordion("Subtitle preview", open=False):
1060
+ with gr.Row():
1061
+ user_source_text = gr.Textbox(label="Original subtitles", lines=8)
1062
+ user_translated_text = gr.Textbox(label="Translated subtitles", lines=8)
1063
+ with gr.Group(elem_classes=["voicegate-card"]):
1064
+ with gr.Accordion("Log", open=False):
1065
+ user_status = gr.Textbox(label="Status", lines=12, elem_classes=["voicegate-status"])
1066
  user_run.click(
1067
  fn=voicegate_user_run,
1068
  inputs=[user_audio, user_target_language, user_tts_trim_start],
 
1115
 
1116
 
1117
  if __name__ == "__main__":
1118
+ demo.launch(theme=voicegate_theme(), css=APP_CSS)