分镜支持视频功能

This commit is contained in:
puke
2025-11-11 20:38:31 +08:00
parent cf9321feac
commit 0e2b6b17d0
17 changed files with 1225 additions and 321 deletions

View File

@@ -782,10 +782,29 @@ def main():
generator_for_params = HTMLFrameGenerator(template_path_for_params)
custom_params_for_video = generator_for_params.parse_template_parameters()
# Detect if template requires image generation
template_requires_image = generator_for_params.requires_image()
# Store in session state for Image Section to read
st.session_state['template_requires_image'] = template_requires_image
# Detect template media type
from pathlib import Path
template_name = Path(frame_template).name
if template_name.startswith("video_"):
# Video template
template_media_type = "video"
template_requires_media = True
elif generator_for_params.requires_image():
# Image template
template_media_type = "image"
template_requires_media = True
else:
# Text-only template
template_media_type = "text"
template_requires_media = False
# Store in session state for workflow filtering
st.session_state['template_media_type'] = template_media_type
st.session_state['template_requires_media'] = template_requires_media
# Backward compatibility
st.session_state['template_requires_image'] = (template_media_type == "image")
custom_values_for_video = {}
if custom_params_for_video:
@@ -928,25 +947,51 @@ def main():
logger.exception(e)
# ====================================================================
# Image Generation Section (conditional based on template)
# Media Generation Section (conditional based on template)
# ====================================================================
# Check if current template requires image generation
if st.session_state.get('template_requires_image', True):
# Template requires images - show full Image Section
# Check if current template requires media generation
template_media_type = st.session_state.get('template_media_type', 'image')
template_requires_media = st.session_state.get('template_requires_media', True)
if template_requires_media:
# Template requires media - show Media Generation Section
with st.container(border=True):
st.markdown(f"**{tr('section.image')}**")
# Dynamic section title based on template type
if template_media_type == "video":
section_title = tr('section.video')
else:
section_title = tr('section.image')
st.markdown(f"**{section_title}**")
# 1. ComfyUI Workflow selection
with st.expander(tr("help.feature_description"), expanded=False):
st.markdown(f"**{tr('help.what')}**")
st.markdown(tr("style.workflow_what"))
if template_media_type == "video":
st.markdown(tr('style.video_workflow_what'))
else:
st.markdown(tr("style.workflow_what"))
st.markdown(f"**{tr('help.how')}**")
st.markdown(tr("style.workflow_how"))
if template_media_type == "video":
st.markdown(tr('style.video_workflow_how'))
else:
st.markdown(tr("style.workflow_how"))
st.markdown(f"**{tr('help.note')}**")
st.markdown(tr("style.image_size_note"))
if template_media_type == "video":
st.markdown(tr('style.video_size_note'))
else:
st.markdown(tr("style.image_size_note"))
# Get available workflows from pixelle_video (with source info)
workflows = pixelle_video.image.list_workflows()
# Get available workflows and filter by template type
all_workflows = pixelle_video.media.list_workflows()
# Filter workflows based on template media type
if template_media_type == "video":
# Only show video_ workflows
workflows = [wf for wf in all_workflows if "video_" in wf["key"].lower()]
else:
# Only show image_ workflows (exclude video_)
workflows = [wf for wf in all_workflows if "video_" not in wf["key"].lower()]
# Build options for selectbox
# Display: "image_flux.json - Runninghub"
@@ -979,25 +1024,39 @@ def main():
workflow_key = "runninghub/image_flux.json" # fallback
# 2. Image size input
# 2. Media size input
col1, col2 = st.columns(2)
with col1:
if template_media_type == "video":
width_label = tr('style.video_width')
width_help = tr('style.video_width_help')
else:
width_label = tr('style.image_width')
width_help = tr('style.image_width_help')
image_width = st.number_input(
tr('style.image_width'),
width_label,
min_value=128,
value=1024,
step=1,
label_visibility="visible",
help=tr('style.image_width_help')
help=width_help
)
with col2:
if template_media_type == "video":
height_label = tr('style.video_height')
height_help = tr('style.video_height_help')
else:
height_label = tr('style.image_height')
height_help = tr('style.image_height_help')
image_height = st.number_input(
tr('style.image_height'),
height_label,
min_value=128,
value=1024,
step=1,
label_visibility="visible",
help=tr('style.image_height_help')
help=height_help
)
# 3. Prompt prefix input
@@ -1014,54 +1073,71 @@ def main():
help=tr("style.prompt_prefix_help")
)
# Style preview expander (similar to template preview)
with st.expander(tr("style.preview_title"), expanded=False):
# Media preview expander
preview_title = tr("style.video_preview_title") if template_media_type == "video" else tr("style.preview_title")
with st.expander(preview_title, expanded=False):
# Test prompt input
if template_media_type == "video":
test_prompt_label = tr("style.test_video_prompt")
test_prompt_value = "a dog running in the park"
else:
test_prompt_label = tr("style.test_prompt")
test_prompt_value = "a dog"
test_prompt = st.text_input(
tr("style.test_prompt"),
value="a dog",
test_prompt_label,
value=test_prompt_value,
help=tr("style.test_prompt_help"),
key="style_test_prompt"
)
# Preview button
if st.button(tr("style.preview"), key="preview_style", use_container_width=True):
with st.spinner(tr("style.previewing")):
preview_button_label = tr("style.video_preview") if template_media_type == "video" else tr("style.preview")
if st.button(preview_button_label, key="preview_style", use_container_width=True):
previewing_text = tr("style.video_previewing") if template_media_type == "video" else tr("style.previewing")
with st.spinner(previewing_text):
try:
from pixelle_video.utils.prompt_helper import build_image_prompt
# Build final prompt with prefix
final_prompt = build_image_prompt(test_prompt, prompt_prefix)
# Generate preview image (use user-specified size)
preview_image_path = run_async(pixelle_video.image(
# Generate preview media (use user-specified size and media type)
media_result = run_async(pixelle_video.media(
prompt=final_prompt,
workflow=workflow_key,
media_type=template_media_type,
width=int(image_width),
height=int(image_height)
))
preview_media_path = media_result.url
# Display preview (support both URL and local path)
if preview_image_path:
st.success(tr("style.preview_success"))
if preview_media_path:
success_text = tr("style.video_preview_success") if template_media_type == "video" else tr("style.preview_success")
st.success(success_text)
# Read and encode image
if preview_image_path.startswith('http'):
# URL - use directly
img_html = f'<div class="preview-image"><img src="{preview_image_path}" alt="Style Preview"/></div>'
if template_media_type == "video":
# Display video
st.video(preview_media_path)
else:
# Local file - encode as base64
with open(preview_image_path, 'rb') as f:
img_data = base64.b64encode(f.read()).decode()
img_html = f'<div class="preview-image"><img src="data:image/png;base64,{img_data}" alt="Style Preview"/></div>'
st.markdown(img_html, unsafe_allow_html=True)
# Display image
if preview_media_path.startswith('http'):
# URL - use directly
img_html = f'<div class="preview-image"><img src="{preview_media_path}" alt="Style Preview"/></div>'
else:
# Local file - encode as base64
with open(preview_media_path, 'rb') as f:
img_data = base64.b64encode(f.read()).decode()
img_html = f'<div class="preview-image"><img src="data:image/png;base64,{img_data}" alt="Style Preview"/></div>'
st.markdown(img_html, unsafe_allow_html=True)
# Show the final prompt used
st.info(f"**{tr('style.final_prompt_label')}**\n{final_prompt}")
# Show file path
st.caption(f"📁 {preview_image_path}")
st.caption(f"📁 {preview_media_path}")
else:
st.error(tr("style.preview_failed_general"))
except Exception as e: