Ryanus commited on
Commit
63b0c66
·
verified ·
1 Parent(s): ab30454

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +101 -107
app.py CHANGED
@@ -82,112 +82,106 @@ def remove_paragraph(paragraphs):
82
  return paragraphs
83
 
84
  def render_paragraphs(paragraphs):
85
- # 回傳一組 Textbox 元件
86
  return [gr.Textbox(value=p, label=f"段落{i+1}內容", lines=3, interactive=True) for i, p in enumerate(paragraphs)]
87
 
88
- def collect_paragraphs(*args):
89
- return list(args)
90
-
91
- async def main():
92
- voices = await get_voices()
93
- with gr.Blocks(theme=gr.themes.Soft()) as demo:
94
- gr.Markdown("## 🎙️ Edge TTS 語音合成與播客製作\n\n- 多段腳本自由增減、內容可清空\n- 介面直覺、操作友善、檔案自動管理")
95
-
96
- with gr.Tab("語音合成"):
97
- with gr.Row():
98
- text_input = gr.Textbox(lines=5, label="輸入文本")
99
- clear_btn = gr.Button("清空")
100
- voice_input = gr.Dropdown(voices, label="選擇語音", value="zh-CN-XiaoxiaoNeural")
101
- rate_input = gr.Slider(-50, 50, value=0, step=1, label="語速調整 (%)")
102
- pitch_input = gr.Slider(-50, 50, value=0, step=1, label="音高調整 (Hz)")
103
- tts_btn = gr.Button("生成語音")
104
- audio_output = gr.Audio(type="filepath", label="生成的語音")
105
- tts_btn.click(
106
- fn=tts_interface,
107
- inputs=[text_input, voice_input, rate_input, pitch_input],
108
- outputs=audio_output
109
- )
110
- clear_btn.click(fn=clear_textbox, outputs=text_input)
111
-
112
- with gr.Tab("檢視已儲存語音"):
113
- audio_files = gr.Dropdown(list_saved_audios(), label="選擇已儲存語音檔案", interactive=True)
114
- saved_audio_output = gr.Audio(type="filepath", label="播放已儲存語音")
115
- audio_files.change(fn=play_saved_audio, inputs=audio_files, outputs=saved_audio_output)
116
-
117
- with gr.Tab("播客製作"):
118
- gr.Markdown("### 📝 多段腳本輸入(可自由增減段落)")
119
- paragraphs_state = gr.State([""])
120
-
121
- # 動態段落區塊
122
- paragraph_column = gr.Column()
123
-
124
- # 初始化
125
- paragraph_boxes = render_paragraphs([""])
126
- for tb in paragraph_boxes:
127
- paragraph_column.append(tb)
128
-
129
- add_btn = gr.Button("新增段落")
130
- remove_btn = gr.Button("刪除段落")
131
- clear_all_btn = gr.Button("全部清空")
132
-
133
- def update_paragraph_ui(paragraphs):
134
- # 重新渲染段落
135
- paragraph_column.children = render_paragraphs(paragraphs)
136
- return gr.update()
137
-
138
- add_btn.click(
139
- lambda p: (add_paragraph(p), update_paragraph_ui(add_paragraph(p))),
140
- inputs=paragraphs_state,
141
- outputs=[paragraphs_state, paragraph_column]
142
- )
143
- remove_btn.click(
144
- lambda p: (remove_paragraph(p), update_paragraph_ui(remove_paragraph(p))),
145
- inputs=paragraphs_state,
146
- outputs=[paragraphs_state, paragraph_column]
147
- )
148
- clear_all_btn.click(
149
- lambda: (clear_paragraphs(), update_paragraph_ui(clear_paragraphs())),
150
- outputs=[paragraphs_state, paragraph_column]
151
- )
152
-
153
- # 參數設定
154
- voice_input2 = gr.Dropdown(voices, label="選擇語音", value="zh-CN-XiaoxiaoNeural")
155
- rate_input2 = gr.Slider(-50, 50, value=0, step=1, label="語速調整 (%)")
156
- pitch_input2 = gr.Slider(-50, 50, value=0, step=1, label="音高調整 (Hz)")
157
- bgm_input = gr.File(label="上傳背景音樂(可選)")
158
- podcast_title = gr.Textbox(label="播客標題")
159
- podcast_desc = gr.Textbox(label="播客描述")
160
- podcast_btn = gr.Button("生成播客")
161
- podcast_output = gr.Audio(type="filepath", label="生成的播客音檔")
162
-
163
- # 播客合成
164
- def on_podcast_btn_click(*args):
165
- n = len(paragraph_column.children)
166
- scripts = list(args[:n])
167
- voice = args[n]
168
- rate = args[n+1]
169
- pitch = args[n+2]
170
- bgm = args[n+3]
171
- title = args[n+4]
172
- desc = args[n+5]
173
- return asyncio.run(podcast_produce(scripts, voice, rate, pitch, bgm, title, desc))
174
-
175
- # 收集 paragraph_column.children 作為 inputs
176
- def get_inputs():
177
- return [tb for tb in paragraph_column.children] + [voice_input2, rate_input2, pitch_input2, bgm_input, podcast_title, podcast_desc]
178
-
179
- podcast_btn.click(
180
- fn=on_podcast_btn_click,
181
- inputs=lambda: get_inputs(),
182
- outputs=podcast_output
183
- )
184
-
185
- with gr.Tab("檢視已儲存播客"):
186
- podcast_files = gr.Dropdown(list_saved_podcasts(), label="選擇已儲存播客檔案", interactive=True)
187
- saved_podcast_output = gr.Audio(type="filepath", label="播放已儲存播客")
188
- podcast_files.change(fn=play_saved_audio, inputs=podcast_files, outputs=saved_podcast_output)
189
-
190
- demo.launch()
191
-
192
- if __name__ == "__main__":
193
- asyncio.run(main())
 
82
  return paragraphs
83
 
84
  def render_paragraphs(paragraphs):
 
85
  return [gr.Textbox(value=p, label=f"段落{i+1}內容", lines=3, interactive=True) for i, p in enumerate(paragraphs)]
86
 
87
+ with gr.Blocks(theme=gr.themes.Soft()) as demo:
88
+ gr.Markdown("## 🎙️ Edge TTS 語音合成與播客製作\n\n- 多段腳本自由增減、內容可清空\n- 介面直覺、操作友善、檔案自動管理")
89
+
90
+ with gr.Tab("語音合成"):
91
+ with gr.Row():
92
+ text_input = gr.Textbox(lines=5, label="輸入文本")
93
+ clear_btn = gr.Button("清空")
94
+ voice_input = gr.Dropdown([], label="選擇語音")
95
+ rate_input = gr.Slider(-50, 50, value=0, step=1, label="語速調整 (%)")
96
+ pitch_input = gr.Slider(-50, 50, value=0, step=1, label="音高調整 (Hz)")
97
+ tts_btn = gr.Button("生成語音")
98
+ audio_output = gr.Audio(type="filepath", label="生成的語音")
99
+ tts_btn.click(
100
+ fn=tts_interface,
101
+ inputs=[text_input, voice_input, rate_input, pitch_input],
102
+ outputs=audio_output
103
+ )
104
+ clear_btn.click(fn=clear_textbox, outputs=text_input)
105
+
106
+ with gr.Tab("檢視已儲存語音"):
107
+ audio_files = gr.Dropdown(list_saved_audios(), label="選擇已儲存語音檔案", interactive=True)
108
+ saved_audio_output = gr.Audio(type="filepath", label="播放已儲存語音")
109
+ audio_files.change(fn=play_saved_audio, inputs=audio_files, outputs=saved_audio_output)
110
+
111
+ with gr.Tab("播客製作"):
112
+ gr.Markdown("### 📝 多段腳本輸入(可自由增減段落)")
113
+ paragraphs_state = gr.State([""])
114
+
115
+ # 用 gr.Column 重新渲染段落
116
+ paragraph_column = gr.Column()
117
+
118
+ # 初始化段落
119
+ def update_paragraph_ui(paragraphs):
120
+ paragraph_column.children = render_paragraphs(paragraphs)
121
+ return gr.update()
122
+
123
+ paragraph_column.children = render_paragraphs([""])
124
+
125
+ add_btn = gr.Button("新增段落")
126
+ remove_btn = gr.Button("刪除段落")
127
+ clear_all_btn = gr.Button("全部清空")
128
+
129
+ add_btn.click(
130
+ lambda p: (add_paragraph(p), update_paragraph_ui(add_paragraph(p))),
131
+ inputs=paragraphs_state,
132
+ outputs=[paragraphs_state, paragraph_column]
133
+ )
134
+ remove_btn.click(
135
+ lambda p: (remove_paragraph(p), update_paragraph_ui(remove_paragraph(p))),
136
+ inputs=paragraphs_state,
137
+ outputs=[paragraphs_state, paragraph_column]
138
+ )
139
+ clear_all_btn.click(
140
+ lambda: (clear_paragraphs(), update_paragraph_ui(clear_paragraphs())),
141
+ outputs=[paragraphs_state, paragraph_column]
142
+ )
143
+
144
+ voice_input2 = gr.Dropdown([], label="選擇語音")
145
+ rate_input2 = gr.Slider(-50, 50, value=0, step=1, label="語速調整 (%)")
146
+ pitch_input2 = gr.Slider(-50, 50, value=0, step=1, label="音高調整 (Hz)")
147
+ bgm_input = gr.File(label="上傳背景音樂(可選)")
148
+ podcast_title = gr.Textbox(label="播客標題")
149
+ podcast_desc = gr.Textbox(label="播客描述")
150
+ podcast_btn = gr.Button("生成播客")
151
+ podcast_output = gr.Audio(type="filepath", label="生成的播客音檔")
152
+
153
+ def on_podcast_btn_click(*args):
154
+ n = len(paragraph_column.children)
155
+ scripts = list(args[:n])
156
+ voice = args[n]
157
+ rate = args[n+1]
158
+ pitch = args[n+2]
159
+ bgm = args[n+3]
160
+ title = args[n+4]
161
+ desc = args[n+5]
162
+ return asyncio.run(podcast_produce(scripts, voice, rate, pitch, bgm, title, desc))
163
+
164
+ def get_inputs():
165
+ return [tb for tb in paragraph_column.children] + [voice_input2, rate_input2, pitch_input2, bgm_input, podcast_title, podcast_desc]
166
+
167
+ podcast_btn.click(
168
+ fn=on_podcast_btn_click,
169
+ inputs=get_inputs,
170
+ outputs=podcast_output
171
+ )
172
+
173
+ with gr.Tab("檢視已儲存播客"):
174
+ podcast_files = gr.Dropdown(list_saved_podcasts(), label="選擇已儲存播客檔案", interactive=True)
175
+ saved_podcast_output = gr.Audio(type="filepath", label="播放已儲存播客")
176
+ podcast_files.change(fn=play_saved_audio, inputs=podcast_files, outputs=saved_podcast_output)
177
+
178
+ # 語音清單初始化
179
+ async def init_voices():
180
+ voices = await get_voices()
181
+ voice_input.choices = voices
182
+ voice_input.value = "zh-CN-XiaoxiaoNeural" if "zh-CN-XiaoxiaoNeural" in voices else voices[0]
183
+ voice_input2.choices = voices
184
+ voice_input2.value = "zh-CN-XiaoxiaoNeural" if "zh-CN-XiaoxiaoNeural" in voices else voices[0]
185
+ asyncio.get_event_loop().run_until_complete(init_voices())
186
+
187
+ demo.launch()