分镜支持视频功能
This commit is contained in:
156
web/app.py
156
web/app.py
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user