import streamlit as st from pathlib import Path from config import SHARED_ASSETS_DIR @st.fragment def render_tab(project, callbacks): """渲染「最終影片合成」標籤頁的 UI。""" _render_video_management_section(project, callbacks) _render_shared_asset_uploader(callbacks) _render_shared_asset_selection(callbacks) st.divider() @st.fragment def _render_video_management_section(project, callbacks): """ 一個私有的輔助函式,用於渲染影片管理的 UI 部分。 """ with st.expander("🎬 管理專案影片素材", expanded=False): paths = project.paths output_dir = paths.get("output", Path("./output")) # 使用 .get 提供預設值 video_files = [] if output_dir.exists(): video_files = sorted( [f for f in output_dir.iterdir() if f.suffix.lower() in ['.mp4', '.mov']], key=lambda f: f.stat().st_mtime, reverse=True ) if not video_files: st.info("專案輸出資料夾 (`/output`) 中目前沒有影片檔。") else: st.markdown("勾選您想要刪除的影片,然後點擊下方的按鈕。") st.checkbox( "全選/取消全選", key="select_all_videos", on_change=callbacks.toggle_all_video_checkboxes, args=(video_files,) ) for video_file in video_files: file_size_mb = video_file.stat().st_size / (1024 * 1024) st.checkbox( f"**{video_file.name}** ({file_size_mb:.2f} MB)", key=f"delete_cb_{video_file.name}" ) st.button( "🟥 確認刪除選取的影片", on_click=callbacks.delete_selected_videos, use_container_width=True, type="primary" ) def _render_shared_asset_selection(callbacks): """ 一個私有的輔助函式,用於渲染共享素材選擇和上傳的 UI。 """ st.subheader("步驟 3.2: 選擇共享影片素材") st.subheader("輔助工具") # 【改動 1】更健壯的檔案列表讀取方式 try: shared_videos = [""] + sorted( [f.name for f in SHARED_ASSETS_DIR.glob('*') if f.suffix.lower() in ['.mp4', '.mov']] ) except Exception as e: st.error(f"無法讀取共享素材庫 '{SHARED_ASSETS_DIR}': {e}") shared_videos = [""] with st.container(border=True): st.markdown("##### 從共享素材庫中選擇影片") c1, c2 = st.columns(2) with c1: logo_selection = st.selectbox("選擇 Logo 影片:", shared_videos, key="logo_select") open_selection = st.selectbox("選擇開場影片:", shared_videos, key="open_select") with c2: end_selection = st.selectbox("選擇結尾影片:", shared_videos, key="end_select") st.subheader("步驟 3.2: 執行最終影片合成") all_videos_selected = all([logo_selection, open_selection, end_selection]) if not all_videos_selected: st.info("請從上方的下拉選單中選擇所有 Logo、開場和結尾影片以啟用合成按鈕。") # 【核心改動】st.button 的呼叫現在非常簡潔 st.button( "🎬 合成最終影片", on_click=callbacks.assemble_final_video, kwargs={ # 只傳遞 UI 直接相關的、最少的資訊 (檔名) "logo_video_name": logo_selection, "open_video_name": open_selection, "end_video_name": end_selection }, disabled=not all_videos_selected, use_container_width=True ) # 將選擇的結果回傳給主函式 return logo_selection, open_selection, end_selection def _render_shared_asset_uploader(callbacks): """一個私有的輔助函式,用於渲染共享素材上傳工具。""" with st.expander("📂 管理與上傳共享素材", expanded=False): st.markdown("這裡上傳的影片將存入 `shared_assets` 公共資料夾,可供所有專案重複使用。") st.file_uploader( "上傳新的影片到共享素材庫", type=["mp4", "mov"], accept_multiple_files=True, label_visibility="collapsed", key="shared_video_uploader", on_change=callbacks.upload_shared_videos )