"""
UI组件模块
"""
import gradio as gr
from typing import List, Tuple
from ..config import (
UI_CONFIG,
EXAMPLE_PROMPTS,
ASPECT_RATIOS
)
from ..utils import handle_file_upload, create_image_html
class UIComponents:
"""UI组件类"""
def __init__(self):
self.delete_buttons = []
def create_header(self) -> gr.HTML:
"""创建页面头部"""
return gr.HTML(f"""
{UI_CONFIG['APP_TITLE']}
{UI_CONFIG['APP_SUBTITLE']}
{UI_CONFIG['APP_DESCRIPTION']}
""")
def create_info_box(self) -> gr.HTML:
"""创建信息提示框"""
return gr.HTML("""
Usage Instructions:
• Get your Veo 3 API Key
👉 here 👈
• Select your model: Choose Veo 3 Fast for more cost-effective video generation (Free test) or Veo 3 Quality for higher-quality videos.
• Enter your prompt (text-to-video) or upload your image (image-to-video).
• Click Generate and wait ~1–2 minutes for processing
""")
def create_input_components(self) -> Tuple[gr.Textbox, gr.Textbox, gr.HTML, gr.File, List[gr.Button], gr.Dropdown, gr.Number, gr.Button]:
"""创建输入组件"""
# API Key输入
api_key = gr.Textbox(
label="API Key",
placeholder="Please enter your KIE AI API Key",
type="password",
elem_classes="api-key-input"
)
# 提示词输入
prompt = gr.Textbox(
label="Video Prompt",
placeholder="Describe the video you want to generate, e.g.: A dog playing in a park...",
lines=3,
value=UI_CONFIG["DEFAULT_PROMPT"],
elem_classes="prompt-input"
)
# 图片上传区域 - 放在提示词和宽高比之间
with gr.Group(elem_classes="image-upload-container"):
gr.Markdown("### 📸 Image Upload (Optional)")
gr.Markdown("*Upload 1 image for image-to-video generation, or leave empty for text-to-video*")
# 自定义图片展示区
image_display = gr.HTML(
value="No image uploaded - will generate from text only
",
elem_id="image-display"
)
# 上传按钮
file_upload = gr.File(
show_label=False,
file_count="single",
file_types=["image"],
type="filepath",
height=120,
)
# 删除按钮组
with gr.Row(elem_id="delete-buttons-row"):
delete_label = gr.Markdown("**Delete Images:**", visible=True, elem_id="delete-label")
delete_buttons = []
for i in range(1): # 最多支持1张图片
btn = gr.Button(f"Delete {i + 1}", visible=True, size="sm", elem_id=f"delete-btn-{i}")
delete_buttons.append(btn)
# 宽高比选择
aspect_ratio = gr.Dropdown(
choices=ASPECT_RATIOS,
value=UI_CONFIG["DEFAULT_ASPECT_RATIO"],
label="Aspect Ratio",
info="16:9 for landscape, 9:16 for portrait"
)
# 种子输入区域 - 使用Row让按钮在输入框右边
with gr.Row():
seeds = gr.Number(
label="Seed (Optional)",
value=10001,
info="Random seed for reproducible results (10000-99999)",
scale=4
)
random_seed_btn = gr.Button(
"🎲",
size="sm",
scale=1,
elem_id="random-seed-btn"
)
# 添加一个状态来保存上传的文件路径
uploaded_file_state = gr.State(None)
return api_key, prompt, image_display, file_upload, delete_buttons, aspect_ratio, seeds, random_seed_btn, uploaded_file_state
def create_output_section(self) -> Tuple[gr.Video, gr.Textbox]:
"""创建输出区域"""
# 输出视频
output_video = gr.Video(
show_label=False,
elem_id="output-video",
height=400,
container=True
)
# 状态信息
status = gr.Textbox(
label="Processing Status",
interactive=False,
lines=2,
value="Ready, please enter a prompt to generate video..."
)
return output_video, status
def create_examples(self) -> gr.Examples:
"""创建示例"""
return gr.Examples(
examples=[[prompt, None] for prompt in EXAMPLE_PROMPTS],
inputs=[], # 将在主应用中设置
label="Video Prompt Examples"
)
def create_generate_button(self) -> gr.Button:
"""创建生成按钮"""
return gr.Button(
"🚀 Start Generation",
variant="primary",
size="lg"
)
def setup_file_upload_handlers(self, file_upload, image_display, delete_buttons, uploaded_file_state):
"""设置文件上传处理器"""
def on_file_upload(new_files):
if not new_files:
return gr.update(value=None), "No image uploaded - will generate from text only
", gr.update(visible=False), None
# 只处理第一张图片
file_path = new_files[0] if isinstance(new_files, list) else new_files
# 显示图片预览,实际上传在生成时进行
html_content = create_image_html([file_path])
return gr.update(value=None), html_content, gr.update(visible=True, value="Delete Image 1"), file_path
file_upload.upload(
fn=on_file_upload,
inputs=[file_upload],
outputs=[file_upload, image_display, delete_buttons[0], uploaded_file_state]
)
def setup_delete_handlers(self, delete_buttons, image_display, uploaded_file_state):
"""设置删除按钮处理器"""
def delete_image():
return "No image uploaded - will generate from text only
", gr.update(visible=False), None
# 绑定删除按钮事件
delete_buttons[0].click(
fn=delete_image,
outputs=[image_display, delete_buttons[0], uploaded_file_state]
)