# Copyright (C) 2025 AIDC-AI # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ Content input components for web UI (left column) """ import streamlit as st from web.i18n import tr from web.utils.async_helpers import get_project_version def render_content_input(): """Render content input section (left column) with batch support""" with st.container(border=True): st.markdown(f"**{tr('section.content_input')}**") # ==================================================================== # Step 1: Batch mode toggle (highest priority) # ==================================================================== batch_mode = st.checkbox( tr("batch.mode_label"), value=False, help=tr("batch.mode_help") ) if not batch_mode: # ================================================================ # Single task mode (original logic, unchanged) # ================================================================ # Processing mode selection mode = st.radio( "Processing Mode", ["generate", "fixed"], horizontal=True, format_func=lambda x: tr(f"mode.{x}"), label_visibility="collapsed" ) # Text input (unified for both modes) text_placeholder = tr("input.topic_placeholder") if mode == "generate" else tr("input.content_placeholder") text_height = 120 if mode == "generate" else 200 text_help = tr("input.text_help_generate") if mode == "generate" else tr("input.text_help_fixed") text = st.text_area( tr("input.text"), placeholder=text_placeholder, height=text_height, help=text_help ) # Title input (optional for both modes) title = st.text_input( tr("input.title"), placeholder=tr("input.title_placeholder"), help=tr("input.title_help") ) # Number of scenes (only show in generate mode) if mode == "generate": n_scenes = st.slider( tr("video.frames"), min_value=3, max_value=30, value=5, help=tr("video.frames_help"), label_visibility="collapsed" ) st.caption(tr("video.frames_label", n=n_scenes)) else: # Fixed mode: n_scenes is ignored, set default value n_scenes = 5 st.info(tr("video.frames_fixed_mode_hint")) return { "batch_mode": False, "mode": mode, "text": text, "title": title, "n_scenes": n_scenes } else: # ================================================================ # Batch mode (simplified YAGNI version) # ================================================================ st.markdown(f"**{tr('batch.section_title')}**") # Batch rules info st.info(f""" **{tr('batch.rules_title')}** - ✅ {tr('batch.rule_1')} - ✅ {tr('batch.rule_2')} - ✅ {tr('batch.rule_3')} """) # Batch topics input text_input = st.text_area( tr("batch.topics_label"), height=300, placeholder=tr("batch.topics_placeholder"), help=tr("batch.topics_help") ) # Split topics by newline if text_input: # Simple split by newline, filter empty lines topics = [ line.strip() for line in text_input.strip().split('\n') if line.strip() ] if topics: # Check count limit if len(topics) > 100: st.error(tr("batch.count_error", count=len(topics))) topics = [] else: st.success(tr("batch.count_success", count=len(topics))) # Preview topics list with st.expander(tr("batch.preview_title"), expanded=False): for i, topic in enumerate(topics, 1): st.markdown(f"`{i}.` {topic}") else: topics = [] else: topics = [] st.markdown("---") # Title prefix (optional) title_prefix = st.text_input( tr("batch.title_prefix_label"), placeholder=tr("batch.title_prefix_placeholder"), help=tr("batch.title_prefix_help") ) # Number of scenes (unified for all videos) n_scenes = st.slider( tr("batch.n_scenes_label"), min_value=3, max_value=30, value=5, help=tr("batch.n_scenes_help") ) st.caption(tr("batch.n_scenes_caption", n=n_scenes)) # Config info st.info(f"📌 {tr('batch.config_info')}") return { "batch_mode": True, "topics": topics, "mode": "generate", # Fixed to AI generate content "title_prefix": title_prefix, "n_scenes": n_scenes, } def render_bgm_section(): """Render BGM selection section""" with st.container(border=True): st.markdown(f"**{tr('section.bgm')}**") with st.expander(tr("help.feature_description"), expanded=False): st.markdown(f"**{tr('help.what')}**") st.markdown(tr("bgm.what")) st.markdown(f"**{tr('help.how')}**") st.markdown(tr("bgm.how")) # Dynamically scan bgm folder for music files (merged from bgm/ and data/bgm/) from pixelle_video.utils.os_util import list_resource_files try: all_files = list_resource_files("bgm") # Filter to audio files only audio_extensions = ('.mp3', '.wav', '.flac', '.m4a', '.aac', '.ogg') bgm_files = sorted([f for f in all_files if f.lower().endswith(audio_extensions)]) except Exception as e: st.warning(f"Failed to load BGM files: {e}") bgm_files = [] # Add special "None" option bgm_options = [tr("bgm.none")] + bgm_files # Default to "default.mp3" if exists, otherwise first option default_index = 0 if "default.mp3" in bgm_files: default_index = bgm_options.index("default.mp3") bgm_choice = st.selectbox( "BGM", bgm_options, index=default_index, label_visibility="collapsed" ) # BGM volume slider (only show when BGM is selected) if bgm_choice != tr("bgm.none"): bgm_volume = st.slider( tr("bgm.volume"), min_value=0.0, max_value=0.5, value=0.2, step=0.01, format="%.2f", key="bgm_volume_slider", help=tr("bgm.volume_help") ) else: bgm_volume = 0.2 # Default value when no BGM selected # BGM preview button (only if BGM is not "None") if bgm_choice != tr("bgm.none"): if st.button(tr("bgm.preview"), key="preview_bgm", use_container_width=True): from pixelle_video.utils.os_util import get_resource_path, resource_exists try: if resource_exists("bgm", bgm_choice): bgm_file_path = get_resource_path("bgm", bgm_choice) st.audio(bgm_file_path) else: st.error(tr("bgm.preview_failed", file=bgm_choice)) except Exception as e: st.error(f"{tr('bgm.preview_failed', file=bgm_choice)}: {e}") # Use full filename for bgm_path (including extension) bgm_path = None if bgm_choice == tr("bgm.none") else bgm_choice return { "bgm_path": bgm_path, "bgm_volume": bgm_volume } def render_version_info(): """Render version info and GitHub link""" with st.container(border=True): st.markdown(f"**{tr('version.title')}**") version = get_project_version() github_url = "https://github.com/AIDC-AI/Pixelle-Video" # Version and GitHub link in one line github_url = "https://github.com/AIDC-AI/Pixelle-Video" badge_url = "https://img.shields.io/github/stars/AIDC-AI/Pixelle-Video" st.markdown( f'{tr("version.current")}: `{version}`    ' f'' f'GitHub stars' f'', unsafe_allow_html=True)