Ryanus commited on
Commit
5cd4332
·
verified ·
1 Parent(s): 9ae478f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +91 -25
app.py CHANGED
@@ -61,7 +61,7 @@ async def podcast_produce(script_list, voice, rate_percentage, pitch_hz, bgm_fil
61
  podcast_audio = sum(audio_segments)
62
  # 插入背景音樂(可選)
63
  if bgm_file is not None and os.path.isfile(bgm_file):
64
- bgm = AudioSegment.from_file(bgm_file).apply_gain(-10)
65
  bgm = bgm[:len(podcast_audio)]
66
  podcast_audio = podcast_audio.overlay(bgm)
67
  # 儲存播客音檔
@@ -73,7 +73,7 @@ async def podcast_produce(script_list, voice, rate_percentage, pitch_hz, bgm_fil
73
  f.write(f"Title: {podcast_title}\nDescription: {podcast_desc}\n")
74
  return podcast_file
75
 
76
- # 動態段落管理
77
  def add_paragraph(paragraphs):
78
  paragraphs.append("")
79
  return paragraphs
@@ -92,7 +92,7 @@ def clear_textbox():
92
  async def main():
93
  voices = await get_voices()
94
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
95
- gr.Markdown("## 🎙️ Edge TTS 語音合成與播客製作\n\n- 支援多段腳本、段落自由增減、內容一鍵清空\n- 介面直覺、操作友善、檔案自動管理")
96
 
97
  with gr.Tab("語音合成"):
98
  with gr.Row():
@@ -117,23 +117,77 @@ async def main():
117
 
118
  with gr.Tab("播客製作"):
119
  gr.Markdown("### 📝 多段腳本輸入(可自由增減段落)")
120
- script_state = gr.State([""])
121
- script_boxes = gr.Group()
122
- def render_script_boxes(paragraphs):
123
- return [gr.Textbox(value=p, label=f"段落{i+1}內容", lines=3) for i, p in enumerate(paragraphs)]
124
- script_boxes.render = render_script_boxes
 
 
 
 
 
 
 
 
125
  add_btn = gr.Button("新增段落")
126
  remove_btn = gr.Button("刪除段落")
127
  clear_all_btn = gr.Button("全部清空")
128
- def update_script_boxes(paragraphs):
129
- return gr.update(components=render_script_boxes(paragraphs))
130
- add_btn.click(fn=add_paragraph, inputs=script_state, outputs=script_state).then(update_script_boxes, script_state, script_boxes)
131
- remove_btn.click(fn=remove_paragraph, inputs=script_state, outputs=script_state).then(update_script_boxes, script_state, script_boxes)
132
- clear_all_btn.click(fn=clear_paragraphs, outputs=script_state).then(update_script_boxes, script_state, script_boxes)
133
- # 取得所有段落內容
134
- def collect_scripts(*args):
135
- return list(args)
136
- # 參數設定
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
  voice_input2 = gr.Dropdown(voices, label="選擇語音", value="zh-CN-XiaoxiaoNeural")
138
  rate_input2 = gr.Slider(-50, 50, value=0, step=1, label="語速調整 (%)")
139
  pitch_input2 = gr.Slider(-50, 50, value=0, step=1, label="音高調整 (Hz)")
@@ -142,14 +196,26 @@ async def main():
142
  podcast_desc = gr.Textbox(label="播客描述")
143
  podcast_btn = gr.Button("生成播客")
144
  podcast_output = gr.Audio(type="filepath", label="生成的播客音檔")
145
- # 合成流程
146
- def get_current_scripts(*args):
147
- return [v for v in args if isinstance(v, str)]
148
- podcast_btn.click(
149
- fn=lambda *args: asyncio.run(podcast_produce(get_current_scripts(*args[:-6]), args[-6], args[-5], args[-4], args[-3], args[-2], args[-1])),
150
- inputs=[*script_boxes, voice_input2, rate_input2, pitch_input2, bgm_input, podcast_title, podcast_desc],
151
- outputs=podcast_output
152
- )
 
 
 
 
 
 
 
 
 
 
 
 
153
 
154
  with gr.Tab("檢視已儲存播客"):
155
  podcast_files = gr.Dropdown(list_saved_podcasts(), label="選擇已儲存播客檔案", interactive=True)
 
61
  podcast_audio = sum(audio_segments)
62
  # 插入背景音樂(可選)
63
  if bgm_file is not None and os.path.isfile(bgm_file):
64
+ bgm = AudioSegment.from_file(bgm_file.name).apply_gain(-10)
65
  bgm = bgm[:len(podcast_audio)]
66
  podcast_audio = podcast_audio.overlay(bgm)
67
  # 儲存播客音檔
 
73
  f.write(f"Title: {podcast_title}\nDescription: {podcast_desc}\n")
74
  return podcast_file
75
 
76
+ # 動態段落管理函數
77
  def add_paragraph(paragraphs):
78
  paragraphs.append("")
79
  return paragraphs
 
92
  async def main():
93
  voices = await get_voices()
94
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
95
+ gr.Markdown("## 🎙️ Edge TTS 語音合成與播客製作\n\n- 多段腳本自由增減、內容可清空\n- 介面直覺、操作友善、檔案自動管理")
96
 
97
  with gr.Tab("語音合成"):
98
  with gr.Row():
 
117
 
118
  with gr.Tab("播客製作"):
119
  gr.Markdown("### 📝 多段腳本輸入(可自由增減段落)")
120
+ # state 儲存段落文字
121
+ paragraphs_state = gr.State([""])
122
+ # 初始一個段落 Textbox
123
+ paragraph_boxes = []
124
+
125
+ def render_paragraphs(paragraphs):
126
+ # 回傳多個 Textbox,value 對應段落內容
127
+ return [gr.Textbox(value=p, label=f"段落{i+1}內容", lines=3, elem_id=f"para_{i}") for i, p in enumerate(paragraphs)]
128
+
129
+ # 初始渲染
130
+ paragraph_boxes = render_paragraphs(paragraphs_state.value)
131
+
132
+ # 按鈕
133
  add_btn = gr.Button("新增段落")
134
  remove_btn = gr.Button("刪除段落")
135
  clear_all_btn = gr.Button("全部清空")
136
+
137
+ # 更新段落文字用
138
+ def update_paragraphs(*texts):
139
+ return list(texts)
140
+
141
+ # 新增段落
142
+ def on_add(paragraphs):
143
+ paragraphs.append("")
144
+ return paragraphs
145
+
146
+ # 刪除段落
147
+ def on_remove(paragraphs):
148
+ if len(paragraphs) > 1:
149
+ paragraphs.pop()
150
+ return paragraphs
151
+
152
+ # 全部清空
153
+ def on_clear():
154
+ return [""]
155
+
156
+ # 連結按鈕與狀態
157
+ add_btn.click(on_add, inputs=paragraphs_state, outputs=paragraphs_state)
158
+ remove_btn.click(on_remove, inputs=paragraphs_state, outputs=paragraphs_state)
159
+ clear_all_btn.click(on_clear, outputs=paragraphs_state)
160
+
161
+ # 監聽段落文字改變,更新狀態
162
+ def on_text_change(*texts):
163
+ return list(texts)
164
+
165
+ # 這裡用一個 container 放段落 Textbox,之後會更新
166
+ paragraphs_container = gr.Column()
167
+ # 用函數渲染段落 Textbox
168
+ def render_paragraphs_ui(paragraphs):
169
+ # 清空 container
170
+ paragraphs_container.clear()
171
+ boxes = []
172
+ for i, p in enumerate(paragraphs):
173
+ tb = gr.Textbox(value=p, label=f"段落{i+1}內容", lines=3)
174
+ boxes.append(tb)
175
+ paragraphs_container.append(tb)
176
+ return boxes
177
+
178
+ # 初始化
179
+ paragraph_boxes = render_paragraphs_ui(paragraphs_state.value)
180
+
181
+ # 當 paragraphs_state 改變時,重新渲染段落輸入框
182
+ def on_paragraphs_state_change(paragraphs):
183
+ # 重新渲染
184
+ nonlocal paragraph_boxes
185
+ paragraph_boxes = render_paragraphs_ui(paragraphs)
186
+ return paragraphs_state
187
+
188
+ paragraphs_state.change(on_paragraphs_state_change, inputs=paragraphs_state, outputs=paragraphs_state)
189
+
190
+ # 播客參數輸入
191
  voice_input2 = gr.Dropdown(voices, label="選擇語音", value="zh-CN-XiaoxiaoNeural")
192
  rate_input2 = gr.Slider(-50, 50, value=0, step=1, label="語速調整 (%)")
193
  pitch_input2 = gr.Slider(-50, 50, value=0, step=1, label="音高調整 (Hz)")
 
196
  podcast_desc = gr.Textbox(label="播客描述")
197
  podcast_btn = gr.Button("生成播客")
198
  podcast_output = gr.Audio(type="filepath", label="生成的播客音檔")
199
+
200
+ # 合成播客按鈕事件
201
+ def gather_paragraph_texts(*args):
202
+ return list(args)
203
+
204
+ # podcast_btn 觸發時,先收集所有段落文字,再呼叫播客合成
205
+ def on_podcast_btn_click(*args):
206
+ # args: 段落文字 + 參數
207
+ n = len(paragraph_boxes)
208
+ scripts = list(args[:n])
209
+ voice = args[n]
210
+ rate = args[n+1]
211
+ pitch = args[n+2]
212
+ bgm = args[n+3]
213
+ title = args[n+4]
214
+ desc = args[n+5]
215
+ return asyncio.run(podcast_produce(scripts, voice, rate, pitch, bgm, title, desc))
216
+
217
+ inputs = paragraph_boxes + [voice_input2, rate_input2, pitch_input2, bgm_input, podcast_title, podcast_desc]
218
+ podcast_btn.click(on_podcast_btn_click, inputs=inputs, outputs=podcast_output)
219
 
220
  with gr.Tab("檢視已儲存播客"):
221
  podcast_files = gr.Dropdown(list_saved_podcasts(), label="選擇已儲存播客檔案", interactive=True)