digital_tts_config.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  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. Style configuration components for web UI (middle column)
  14. """
  15. import os
  16. from pathlib import Path
  17. import streamlit as st
  18. from loguru import logger
  19. from web.i18n import tr, get_language
  20. from web.utils.async_helpers import run_async
  21. from pixelle_video.config import config_manager
  22. def render_style_config(pixelle_video):
  23. """Render style configuration section (middle column)"""
  24. # TTS Section (moved from left column)
  25. # ====================================================================
  26. with st.container(border=True):
  27. st.markdown(f"**{tr('section.tts')}**")
  28. with st.expander(tr("help.feature_description"), expanded=False):
  29. st.markdown(f"**{tr('help.what')}**")
  30. st.markdown(tr("tts.what"))
  31. st.markdown(f"**{tr('help.how')}**")
  32. st.markdown(tr("tts.how"))
  33. # Get TTS config
  34. comfyui_config = config_manager.get_comfyui_config()
  35. tts_config = comfyui_config["tts"]
  36. # Inference mode selection
  37. tts_mode = st.radio(
  38. tr("tts.inference_mode"),
  39. ["local", "comfyui"],
  40. horizontal=True,
  41. format_func=lambda x: tr(f"tts.mode.{x}"),
  42. index=0 if tts_config.get("inference_mode", "local") == "local" else 1,
  43. key="digital_tts_inference_mode"
  44. )
  45. # Show hint based on mode
  46. if tts_mode == "local":
  47. st.caption(tr("tts.mode.local_hint"))
  48. else:
  49. st.caption(tr("tts.mode.comfyui_hint"))
  50. # ================================================================
  51. # Local Mode UI
  52. # ================================================================
  53. if tts_mode == "local":
  54. # Import voice configuration
  55. from pixelle_video.tts_voices import EDGE_TTS_VOICES, get_voice_display_name
  56. # Get saved voice from config
  57. local_config = tts_config.get("local", {})
  58. saved_voice = local_config.get("voice", "zh-CN-YunjianNeural")
  59. saved_speed = local_config.get("speed", 1.2)
  60. # Build voice options with i18n
  61. voice_options = []
  62. voice_ids = []
  63. default_voice_index = 0
  64. for idx, voice_config in enumerate(EDGE_TTS_VOICES):
  65. voice_id = voice_config["id"]
  66. display_name = get_voice_display_name(voice_id, tr, get_language())
  67. voice_options.append(display_name)
  68. voice_ids.append(voice_id)
  69. # Set default index if matches saved voice
  70. if voice_id == saved_voice:
  71. default_voice_index = idx
  72. # Two-column layout: Voice | Speed
  73. voice_col, speed_col = st.columns([1, 1])
  74. with voice_col:
  75. # Voice selector
  76. selected_voice_display = st.selectbox(
  77. tr("tts.voice_selector"),
  78. voice_options,
  79. index=default_voice_index,
  80. key="digital_tts_local_voice"
  81. )
  82. # Get actual voice ID
  83. selected_voice_index = voice_options.index(selected_voice_display)
  84. selected_voice = voice_ids[selected_voice_index]
  85. with speed_col:
  86. # Speed slider
  87. tts_speed = st.slider(
  88. tr("tts.speed"),
  89. min_value=0.5,
  90. max_value=2.0,
  91. value=saved_speed,
  92. step=0.1,
  93. format="%.1fx",
  94. key="digital_tts_local_speed"
  95. )
  96. st.caption(tr("tts.speed_label", speed=f"{tts_speed:.1f}"))
  97. # Variables for video generation
  98. tts_workflow_key = None
  99. ref_audio_path = None
  100. # ================================================================
  101. # ComfyUI Mode UI
  102. # ================================================================
  103. else: # comfyui mode
  104. tts_workflow_key = "runninghub/tts_index2.json" # fallback
  105. # Reference audio upload (optional, for voice cloning)
  106. ref_audio_file = st.file_uploader(
  107. tr("tts.ref_audio"),
  108. type=["mp3", "wav", "flac", "m4a", "aac", "ogg"],
  109. help=tr("tts.ref_audio_help"),
  110. key="digital_ref_audio_upload"
  111. )
  112. # Save uploaded ref_audio to temp file if provided
  113. ref_audio_path = None
  114. if ref_audio_file is not None:
  115. # Audio preview player (directly play uploaded file)
  116. st.audio(ref_audio_file)
  117. # Save to temp directory
  118. temp_dir = Path("temp")
  119. temp_dir.mkdir(exist_ok=True)
  120. ref_audio_path = temp_dir / f"ref_audio_{ref_audio_file.name}"
  121. with open(ref_audio_path, "wb") as f:
  122. f.write(ref_audio_file.getbuffer())
  123. # Variables for video generation
  124. selected_voice = None
  125. tts_speed = None
  126. # ================================================================
  127. # TTS Preview (works for both modes)
  128. # ================================================================
  129. with st.expander(tr("tts.preview_title"), expanded=False):
  130. # Preview text input
  131. preview_text = st.text_input(
  132. tr("tts.preview_text"),
  133. value="大家好,这是一段测试语音。",
  134. placeholder=tr("tts.preview_text_placeholder"),
  135. key="digital_tts_preview_text"
  136. )
  137. # Preview button
  138. if st.button(tr("tts.preview_button"), key="gidital_preview_tts", use_container_width=True):
  139. with st.spinner(tr("tts.previewing")):
  140. try:
  141. # Build TTS params based on mode
  142. tts_params = {
  143. "text": preview_text,
  144. "inference_mode": tts_mode
  145. }
  146. if tts_mode == "local":
  147. tts_params["voice"] = selected_voice
  148. tts_params["speed"] = tts_speed
  149. else: # comfyui
  150. tts_params["workflow"] = tts_workflow_key
  151. if ref_audio_path:
  152. tts_params["ref_audio"] = str(ref_audio_path)
  153. audio_path = run_async(pixelle_video.tts(**tts_params))
  154. # Play the audio
  155. if audio_path:
  156. st.success(tr("tts.preview_success"))
  157. if os.path.exists(audio_path):
  158. st.audio(audio_path, format="audio/mp3")
  159. elif audio_path.startswith('http'):
  160. st.audio(audio_path)
  161. else:
  162. st.error("Failed to generate preview audio")
  163. # Show file path
  164. st.caption(f"📁 {audio_path}")
  165. else:
  166. st.error("Failed to generate preview audio")
  167. except Exception as e:
  168. st.error(tr("tts.preview_failed", error=str(e)))
  169. logger.exception(e)
  170. # Return all style configuration parameters (Simplified version only local TTS)
  171. return {
  172. "tts_inference_mode": tts_mode,
  173. "tts_voice": selected_voice if tts_mode == "local" else None,
  174. "tts_speed": tts_speed if tts_mode == "local" else None,
  175. "tts_workflow": tts_workflow_key if tts_mode == "comfyui" else None,
  176. "ref_audio": str(ref_audio_path) if ref_audio_path else None,
  177. }