version 1

This commit is contained in:
2025-07-15 14:11:39 +08:00
parent 81d4874926
commit dc94cc41c2
16 changed files with 1872 additions and 445 deletions

View File

@ -0,0 +1,142 @@
# utils/tab2_search_video.py
import streamlit as st
import os
from pathlib import Path
from utils.helpers import analyze_ass_for_keywords, search_pixabay_videos, search_pexels_videos
@st.fragment
def render_tab(project,callbacks):
"""一個用於從多個來源搜尋並下載影片的獨立UI片段。"""
paths = project.paths
st.header("步驟 5: 從線上圖庫搜尋影片素材")
pixabay_api_key = st.secrets.get("PIXABAY_API_KEY")
pexels_api_key = st.secrets.get("PEXELS_API_KEY")
if not pixabay_api_key and not pexels_api_key:
st.warning("請至少在 Streamlit secrets 中設定 `PIXABAY_API_KEY` 或 `PEXELS_API_KEY` 以使用此功能。")
return
st.subheader("5.1 搜尋影片")
# 【新增】影片來源選擇
available_sources = []
if pixabay_api_key: available_sources.append("Pixabay")
if pexels_api_key: available_sources.append("Pexels")
if not available_sources:
st.session_state.operation_status = {
"success": False,
"message": "沒有可用的影片來源 API 金鑰。"
}
return
source_choice = st.radio(
"選擇影片來源:",
options=available_sources,
horizontal=True,
key="video_source_choice"
)
keywords = analyze_ass_for_keywords(paths)
default_query = keywords[0] if keywords else ""
with st.form(key="search_form"):
col1, col2 = st.columns([4, 1])
with col1:
st.text_input(
"Search Videos",
value=default_query,
key="search_query_input",
placeholder="輸入關鍵字後按 Enter 或點擊右側按鈕搜尋",
label_visibility="collapsed"
)
with col2:
submitted = st.form_submit_button("🔍 搜尋影片", use_container_width=True)
if submitted:
with st.spinner("正在搜尋並過濾影片..."):
query = st.session_state.get("search_query_input", "")
# 【修改】根據選擇呼叫對應的 API
if source_choice == "Pixabay":
success, message, results = search_pixabay_videos(pixabay_api_key, query)
elif source_choice == "Pexels":
success, message, results = search_pexels_videos(pexels_api_key, query)
else:
success, message, results = False, "未知的影片來源", []
st.session_state.operation_status = {"success": success, "message": message, "source": "search_videos"}
# --- 【修改】資料標準化 ---
standardized_results = []
if success and results:
if source_choice == "Pixabay":
for v in results:
try:
standardized_results.append({
'id': f"pixabay-{v['id']}",
'thumbnail_url': v['videos']['tiny']['thumbnail'],
'video_url': v['videos']['large']['url'],
'preview_url': v['videos']['tiny']['url'],
'width': v['videos']['large']['width'],
'height': v['videos']['large']['height']
})
except KeyError: continue
elif source_choice == "Pexels":
for v in results:
try:
# 尋找合適的影片檔案連結
video_link_hd = next((f['link'] for f in v['video_files'] if f.get('quality') == 'hd'), None)
video_link_sd = next((f['link'] for f in v['video_files'] if f.get('quality') == 'sd'), None)
# 優先使用 HD 畫質,若無則用 SD再沒有就用第一個
final_video_url = video_link_hd or video_link_sd or v['video_files'][0]['link']
standardized_results.append({
'id': f"pexels-{v['id']}",
'thumbnail_url': v['image'],
'video_url': final_video_url,
'preview_url': video_link_sd or final_video_url, # 預覽用 SD 或更高畫質
'width': v['width'],
'height': v['height']
})
except (KeyError, IndexError): continue
st.session_state.search_results = standardized_results
st.session_state.selected_videos = {str(v['id']): {"url": v['video_url'], "selected": False} for v in standardized_results}
st.session_state.active_preview_id = None
if keywords and (keywords[1] or keywords[2]):
st.caption(f"建議關鍵字: `{keywords[1]}` `{keywords[2]}`")
st.divider()
if st.session_state.search_results:
st.subheader("5.2 選擇影片並下載")
st.button("📥 下載選取的影片到專案素材庫", on_click=callbacks.callback_download_videos, args=(paths,), help="將下方勾選的影片下載至目前專案的 `output/test` 資料夾,並自動循序命名。")
num_cols = 5
cols = st.columns(num_cols)
# 【修改】使用標準化後的結果進行渲染
for i, video in enumerate(st.session_state.search_results):
with cols[i % num_cols]:
with st.container(border=True):
media_placeholder = st.empty()
video_id_str = str(video['id'])
if st.session_state.active_preview_id == video_id_str:
media_placeholder.video(video['preview_url'])
preview_button_text, preview_button_type = "⏹️ 停止預覽", "secondary"
else:
media_placeholder.image(video['thumbnail_url'], use_container_width=True)
preview_button_text, preview_button_type = "▶️ 預覽", "primary"
st.caption(f"ID: {video_id_str}")
st.caption(f"尺寸: {video['width']}x{video['height']}")
is_selected = st.checkbox("選取", key=f"select_{video_id_str}", value=st.session_state.selected_videos.get(video_id_str, {}).get('selected', False))
if video_id_str in st.session_state.selected_videos:
st.session_state.selected_videos[video_id_str]['selected'] = is_selected
st.button(preview_button_text, key=f"preview_{video_id_str}", on_click=callbacks.callback_toggle_preview, args=(video_id_str,), use_container_width=True, type=preview_button_type)