返回 Pixelle-Video
content_input.py
根目录 / web / components / content_input.py
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 """
14 Content input components for web UI (left column)
15 """
16
17 import streamlit as st
18
19 from web.i18n import tr
20 from web.utils.async_helpers import get_project_version
21
22
23 def render_content_input():
24 """Render content input section (left column) with batch support"""
25 with st.container(border=True):
26 st.markdown(f"**{tr('section.content_input')}**")
27
28 # ====================================================================
29 # Step 1: Batch mode toggle (highest priority)
30 # ====================================================================
31 batch_mode = st.checkbox(
32 tr("batch.mode_label"),
33 value=False,
34 help=tr("batch.mode_help")
35 )
36
37 if not batch_mode:
38 # ================================================================
39 # Single task mode (original logic, unchanged)
40 # ================================================================
41 # Processing mode selection
42 mode = st.radio(
43 "Processing Mode",
44 ["generate", "fixed"],
45 horizontal=True,
46 format_func=lambda x: tr(f"mode.{x}"),
47 label_visibility="collapsed"
48 )
49
50 # Text input (unified for both modes)
51 text_placeholder = tr("input.topic_placeholder") if mode == "generate" else tr("input.content_placeholder")
52 text_height = 120 if mode == "generate" else 200
53 text_help = tr("input.text_help_generate") if mode == "generate" else tr("input.text_help_fixed")
54
55 text = st.text_area(
56 tr("input.text"),
57 placeholder=text_placeholder,
58 height=text_height,
59 help=text_help
60 )
61
62 # Split mode selector (only show in fixed mode)
63 if mode == "fixed":
64 split_mode_options = {
65 "paragraph": tr("split.mode_paragraph"),
66 "line": tr("split.mode_line"),
67 "sentence": tr("split.mode_sentence"),
68 }
69 split_mode = st.selectbox(
70 tr("split.mode_label"),
71 options=list(split_mode_options.keys()),
72 format_func=lambda x: split_mode_options[x],
73 index=0, # Default to paragraph mode
74 help=tr("split.mode_help")
75 )
76 else:
77 split_mode = "paragraph" # Default for generate mode (not used)
78
79 # Title input (optional for both modes)
80 title = st.text_input(
81 tr("input.title"),
82 placeholder=tr("input.title_placeholder"),
83 help=tr("input.title_help")
84 )
85
86 # Number of scenes (only show in generate mode)
87 if mode == "generate":
88 n_scenes = st.slider(
89 tr("video.frames"),
90 min_value=3,
91 max_value=30,
92 value=5,
93 help=tr("video.frames_help"),
94 label_visibility="collapsed"
95 )
96 st.caption(tr("video.frames_label", n=n_scenes))
97 else:
98 # Fixed mode: n_scenes is ignored, set default value
99 n_scenes = 5
100 st.info(tr("video.frames_fixed_mode_hint"))
101
102 return {
103 "batch_mode": False,
104 "mode": mode,
105 "text": text,
106 "title": title,
107 "n_scenes": n_scenes,
108 "split_mode": split_mode
109 }
110
111 else:
112 # ================================================================
113 # Batch mode (simplified YAGNI version)
114 # ================================================================
115 st.markdown(f"**{tr('batch.section_title')}**")
116
117 # Batch rules info
118 st.info(f"""
119 **{tr('batch.rules_title')}**
120 - ✅ {tr('batch.rule_1')}
121 - ✅ {tr('batch.rule_2')}
122 - ✅ {tr('batch.rule_3')}
123 """)
124
125 # Batch topics input
126 text_input = st.text_area(
127 tr("batch.topics_label"),
128 height=300,
129 placeholder=tr("batch.topics_placeholder"),
130 help=tr("batch.topics_help")
131 )
132
133 # Split topics by newline
134 if text_input:
135 # Simple split by newline, filter empty lines
136 topics = [
137 line.strip()
138 for line in text_input.strip().split('\n')
139 if line.strip()
140 ]
141
142 if topics:
143 # Check count limit
144 if len(topics) > 100:
145 st.error(tr("batch.count_error", count=len(topics)))
146 topics = []
147 else:
148 st.success(tr("batch.count_success", count=len(topics)))
149
150 # Preview topics list
151 with st.expander(tr("batch.preview_title"), expanded=False):
152 for i, topic in enumerate(topics, 1):
153 st.markdown(f"`{i}.` {topic}")
154 else:
155 topics = []
156 else:
157 topics = []
158
159 st.markdown("---")
160
161 # Title prefix (optional)
162 title_prefix = st.text_input(
163 tr("batch.title_prefix_label"),
164 placeholder=tr("batch.title_prefix_placeholder"),
165 help=tr("batch.title_prefix_help")
166 )
167
168 # Number of scenes (unified for all videos)
169 n_scenes = st.slider(
170 tr("batch.n_scenes_label"),
171 min_value=3,
172 max_value=30,
173 value=5,
174 help=tr("batch.n_scenes_help")
175 )
176 st.caption(tr("batch.n_scenes_caption", n=n_scenes))
177
178 # Config info
179 st.info(f"📌 {tr('batch.config_info')}")
180
181 return {
182 "batch_mode": True,
183 "topics": topics,
184 "mode": "generate", # Fixed to AI generate content
185 "title_prefix": title_prefix,
186 "n_scenes": n_scenes,
187 }
188
189
190 def render_bgm_section(key_prefix=""):
191 """Render BGM selection section"""
192 with st.container(border=True):
193 st.markdown(f"**{tr('section.bgm')}**")
194
195 with st.expander(tr("help.feature_description"), expanded=False):
196 st.markdown(f"**{tr('help.what')}**")
197 st.markdown(tr("bgm.what"))
198 st.markdown(f"**{tr('help.how')}**")
199 st.markdown(tr("bgm.how"))
200
201 # Dynamically scan bgm folder for music files (merged from bgm/ and data/bgm/)
202 from pixelle_video.utils.os_util import list_resource_files
203
204 try:
205 all_files = list_resource_files("bgm")
206 # Filter to audio files only
207 audio_extensions = ('.mp3', '.wav', '.flac', '.m4a', '.aac', '.ogg')
208 bgm_files = sorted([f for f in all_files if f.lower().endswith(audio_extensions)])
209 except Exception as e:
210 st.warning(f"Failed to load BGM files: {e}")
211 bgm_files = []
212
213 # Add special "None" option
214 bgm_options = [tr("bgm.none")] + bgm_files
215
216 # Default to "default.mp3" if exists, otherwise first option
217 default_index = 0
218 if "default.mp3" in bgm_files:
219 default_index = bgm_options.index("default.mp3")
220
221 bgm_choice = st.selectbox(
222 "BGM",
223 bgm_options,
224 index=default_index,
225 label_visibility="collapsed",
226 key=f"{key_prefix}bgm_selector"
227 )
228
229 # BGM volume slider (only show when BGM is selected)
230 if bgm_choice != tr("bgm.none"):
231 bgm_volume = st.slider(
232 tr("bgm.volume"),
233 min_value=0.0,
234 max_value=0.5,
235 value=0.2,
236 step=0.01,
237 format="%.2f",
238 key=f"{key_prefix}bgm_volume_slider",
239 help=tr("bgm.volume_help")
240 )
241 else:
242 bgm_volume = 0.2 # Default value when no BGM selected
243
244 # BGM preview button (only if BGM is not "None")
245 if bgm_choice != tr("bgm.none"):
246 if st.button(tr("bgm.preview"), key=f"{key_prefix}preview_bgm", use_container_width=True):
247 from pixelle_video.utils.os_util import get_resource_path, resource_exists
248 try:
249 if resource_exists("bgm", bgm_choice):
250 bgm_file_path = get_resource_path("bgm", bgm_choice)
251 st.audio(bgm_file_path)
252 else:
253 st.error(tr("bgm.preview_failed", file=bgm_choice))
254 except Exception as e:
255 st.error(f"{tr('bgm.preview_failed', file=bgm_choice)}: {e}")
256
257 # Use full filename for bgm_path (including extension)
258 bgm_path = None if bgm_choice == tr("bgm.none") else bgm_choice
259
260 return {
261 "bgm_path": bgm_path,
262 "bgm_volume": bgm_volume
263 }
264
265
266 def render_version_info():
267 """Render version info and GitHub link"""
268 with st.container(border=True):
269 st.markdown(f"**{tr('version.title')}**")
270 version = get_project_version()
271 github_url = "https://github.com/AIDC-AI/Pixelle-Video"
272
273 # Version and GitHub link in one line
274 github_url = "https://github.com/AIDC-AI/Pixelle-Video"
275 badge_url = "https://img.shields.io/github/stars/AIDC-AI/Pixelle-Video"
276
277 st.markdown(
278 f'{tr("version.current")}: `{version}`    '
279 f'<a href="{github_url}" target="_blank">'
280 f'<img src="{badge_url}" alt="GitHub stars" style="vertical-align: middle;">'
281 f'</a>',
282 unsafe_allow_html=True)
283
284
284 lines PYTHON