返回 Pixelle-Video
digital_tts_config.py
根目录 / web / components / digital_tts_config.py
1 # Copyright (C) 2025 AIDC-AI
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 # http://www.apache.org/licenses/LICENSE-2.0
7 # Unless required by applicable law or agreed to in writing, software
8 # distributed under the License is distributed on an "AS IS" BASIS,
9 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 # See the License for the specific language governing permissions and
11 # limitations under the License.
12
13 """
14 Style configuration components for web UI (middle column)
15 """
16
17 import os
18 from pathlib import Path
19
20 import streamlit as st
21 from loguru import logger
22
23 from web.i18n import tr, get_language
24 from web.utils.async_helpers import run_async
25 from pixelle_video.config import config_manager
26
27
28 def render_style_config(pixelle_video):
29 """Render style configuration section (middle column)"""
30 # TTS Section (moved from left column)
31 # ====================================================================
32 with st.container(border=True):
33 st.markdown(f"**{tr('section.tts')}**")
34
35 with st.expander(tr("help.feature_description"), expanded=False):
36 st.markdown(f"**{tr('help.what')}**")
37 st.markdown(tr("tts.what"))
38 st.markdown(f"**{tr('help.how')}**")
39 st.markdown(tr("tts.how"))
40
41 # Get TTS config
42 comfyui_config = config_manager.get_comfyui_config()
43 tts_config = comfyui_config["tts"]
44
45 # Inference mode selection
46 tts_mode = st.radio(
47 tr("tts.inference_mode"),
48 ["local", "comfyui"],
49 horizontal=True,
50 format_func=lambda x: tr(f"tts.mode.{x}"),
51 index=0 if tts_config.get("inference_mode", "local") == "local" else 1,
52 key="digital_tts_inference_mode"
53 )
54
55 # Show hint based on mode
56 if tts_mode == "local":
57 st.caption(tr("tts.mode.local_hint"))
58 else:
59 st.caption(tr("tts.mode.comfyui_hint"))
60
61 # ================================================================
62 # Local Mode UI
63 # ================================================================
64 if tts_mode == "local":
65 # Import voice configuration
66 from pixelle_video.tts_voices import EDGE_TTS_VOICES, get_voice_display_name
67
68 # Get saved voice from config
69 local_config = tts_config.get("local", {})
70 saved_voice = local_config.get("voice", "zh-CN-YunjianNeural")
71 saved_speed = local_config.get("speed", 1.2)
72
73 # Build voice options with i18n
74 voice_options = []
75 voice_ids = []
76 default_voice_index = 0
77
78 for idx, voice_config in enumerate(EDGE_TTS_VOICES):
79 voice_id = voice_config["id"]
80 display_name = get_voice_display_name(voice_id, tr, get_language())
81 voice_options.append(display_name)
82 voice_ids.append(voice_id)
83
84 # Set default index if matches saved voice
85 if voice_id == saved_voice:
86 default_voice_index = idx
87
88 # Two-column layout: Voice | Speed
89 voice_col, speed_col = st.columns([1, 1])
90
91 with voice_col:
92 # Voice selector
93 selected_voice_display = st.selectbox(
94 tr("tts.voice_selector"),
95 voice_options,
96 index=default_voice_index,
97 key="digital_tts_local_voice"
98 )
99
100 # Get actual voice ID
101 selected_voice_index = voice_options.index(selected_voice_display)
102 selected_voice = voice_ids[selected_voice_index]
103
104 with speed_col:
105 # Speed slider
106 tts_speed = st.slider(
107 tr("tts.speed"),
108 min_value=0.5,
109 max_value=2.0,
110 value=saved_speed,
111 step=0.1,
112 format="%.1fx",
113 key="digital_tts_local_speed"
114 )
115 st.caption(tr("tts.speed_label", speed=f"{tts_speed:.1f}"))
116
117 # Variables for video generation
118 tts_workflow_key = None
119 ref_audio_path = None
120
121 # ================================================================
122 # ComfyUI Mode UI
123 # ================================================================
124 else: # comfyui mode
125 tts_workflow_key = "runninghub/tts_index2.json" # fallback
126
127 # Reference audio upload (optional, for voice cloning)
128 ref_audio_file = st.file_uploader(
129 tr("tts.ref_audio"),
130 type=["mp3", "wav", "flac", "m4a", "aac", "ogg"],
131 help=tr("tts.ref_audio_help"),
132 key="digital_ref_audio_upload"
133 )
134
135 # Save uploaded ref_audio to temp file if provided
136 ref_audio_path = None
137 if ref_audio_file is not None:
138 # Audio preview player (directly play uploaded file)
139 st.audio(ref_audio_file)
140
141 # Save to temp directory
142 temp_dir = Path("temp")
143 temp_dir.mkdir(exist_ok=True)
144 ref_audio_path = temp_dir / f"ref_audio_{ref_audio_file.name}"
145 with open(ref_audio_path, "wb") as f:
146 f.write(ref_audio_file.getbuffer())
147
148 # Variables for video generation
149 selected_voice = None
150 tts_speed = None
151
152 # ================================================================
153 # TTS Preview (works for both modes)
154 # ================================================================
155 with st.expander(tr("tts.preview_title"), expanded=False):
156 # Preview text input
157 preview_text = st.text_input(
158 tr("tts.preview_text"),
159 value="大家好,这是一段测试语音。",
160 placeholder=tr("tts.preview_text_placeholder"),
161 key="digital_tts_preview_text"
162 )
163
164 # Preview button
165 if st.button(tr("tts.preview_button"), key="gidital_preview_tts", use_container_width=True):
166 with st.spinner(tr("tts.previewing")):
167 try:
168 # Build TTS params based on mode
169 tts_params = {
170 "text": preview_text,
171 "inference_mode": tts_mode
172 }
173
174 if tts_mode == "local":
175 tts_params["voice"] = selected_voice
176 tts_params["speed"] = tts_speed
177 else: # comfyui
178 tts_params["workflow"] = tts_workflow_key
179 if ref_audio_path:
180 tts_params["ref_audio"] = str(ref_audio_path)
181
182 audio_path = run_async(pixelle_video.tts(**tts_params))
183
184 # Play the audio
185 if audio_path:
186 st.success(tr("tts.preview_success"))
187 if os.path.exists(audio_path):
188 st.audio(audio_path, format="audio/mp3")
189 elif audio_path.startswith('http'):
190 st.audio(audio_path)
191 else:
192 st.error("Failed to generate preview audio")
193
194 # Show file path
195 st.caption(f"📁 {audio_path}")
196 else:
197 st.error("Failed to generate preview audio")
198 except Exception as e:
199 st.error(tr("tts.preview_failed", error=str(e)))
200 logger.exception(e)
201
202 # Return all style configuration parameters (Simplified version only local TTS)
203 return {
204 "tts_inference_mode": tts_mode,
205 "tts_voice": selected_voice if tts_mode == "local" else None,
206 "tts_speed": tts_speed if tts_mode == "local" else None,
207 "tts_workflow": tts_workflow_key if tts_mode == "comfyui" else None,
208 "ref_audio": str(ref_audio_path) if ref_audio_path else None,
209 }
209 lines PYTHON