# utils/tab1_asset_generation.py import streamlit as st from utils.helpers import display_operation_status @st.fragment def render_tab(project, callbacks): """ 渲染「素材生成」標籤頁的 UI。 這個函式負責處理所有與音效生成和字幕檔生成相關的介面元件。 Args: project: 存放專案狀態和路徑的物件。 callbacks: 存放按鈕回呼函式的物件。 """ display_operation_status() st.subheader("素材生成") # --- 音效生成區塊 --- with st.container(border=True): st.subheader("音效生成") st.markdown("生成單句音訊") st.button("執行生成", on_click=callbacks.callback_generate_sentence_audio) st.divider() # 步驟 3.2 st.markdown("組合完整音訊") can_concatenate = project.has_sentence_audio() if not can_concatenate: st.info("請先執行生成單句音訊。") st.button("執行組合", on_click=callbacks.callback_concatenate_audio, disabled=not can_concatenate) if project.has_combined_audio(): st.session_state.operation_status = { "success": True, "message": "🎉 完整音訊已組合成功!" } st.audio(str(project.paths['combined_audio'])) audio_management_fragment(project,callbacks) st.divider() # --- 字幕生成區塊 --- with st.container(border=True): st.subheader("生成 ASS 字幕檔") can_generate_ass = project.has_sentence_audio() if not can_generate_ass: st.info("請先執行生成單句音訊 以生成字幕所需的時間戳。") st.button("📝 生成 .ass 字幕檔", on_click=callbacks.callback_generate_subtitles, disabled=not can_generate_ass) st.divider() @st.fragment def audio_management_fragment(project,callbacks): """A self-contained fragment for managing and deleting project audio files.""" with st.expander("🎧 管理專案音訊素材"): paths = project.paths audio_dir = paths["audio"] output_dir = paths["output"] audio_files = [] if audio_dir.exists(): audio_files.extend(sorted(audio_dir.glob('*.wav'))) if output_dir.exists(): audio_files.extend(sorted(output_dir.glob('*.wav'))) if not audio_files: st.info("專案的 `audio` 和 `output` 資料夾中目前沒有音訊檔。") else: all_selected = all(st.session_state.get(f"delete_audio_cb_{f.name}", False) for f in audio_files) if st.session_state.get('select_all_audios', False) != all_selected: st.session_state.select_all_audios = all_selected st.markdown("勾選您想要刪除的音訊檔案,然後點擊下方的按鈕。") st.checkbox( "全選/取消全選", key="select_all_audios", on_change=callbacks.toggle_all_audio_checkboxes, args=(audio_files,) ) with st.form("delete_audios_form"): for audio_file in audio_files: file_size_kb = audio_file.stat().st_size / 1024 label = f"**{audio_file.parent.name}/{audio_file.name}** ({file_size_kb:.2f} KB)" st.checkbox(label, key=f"delete_audio_cb_{audio_file.name}") submitted = st.form_submit_button("🟥 確認刪除選取的音訊", use_container_width=True, type="primary") if submitted: callbacks.callback_delete_selected_audios(paths) st.rerun(scope="fragment")