webui适配视频功能; 统一模板命名规范;
This commit is contained in:
@@ -117,9 +117,10 @@ class CustomPipeline(BasePipeline):
|
||||
VideoGenerationResult
|
||||
|
||||
Image Generation Logic:
|
||||
- If template has {{image}} → automatically generates images
|
||||
- If template has no {{image}} → skips image generation (faster, cheaper)
|
||||
- To customize: Override the template_requires_image logic in your subclass
|
||||
- image_*.html templates → automatically generates images
|
||||
- video_*.html templates → automatically generates videos
|
||||
- static_*.html templates → skips media generation (faster, cheaper)
|
||||
- To customize: Override the template type detection logic in your subclass
|
||||
"""
|
||||
logger.info("Starting CustomPipeline")
|
||||
logger.info(f"Input text length: {len(text)} chars")
|
||||
@@ -151,23 +152,27 @@ class CustomPipeline(BasePipeline):
|
||||
frame_template = template_config.get("default_template", "1080x1920/default.html")
|
||||
|
||||
# ========== Step 0.5: Check template requirements ==========
|
||||
# Detect if template requires {{image}} parameter
|
||||
# This allows skipping the entire image generation pipeline for text-only templates
|
||||
# Detect template type by filename prefix
|
||||
from pathlib import Path
|
||||
from pixelle_video.services.frame_html import HTMLFrameGenerator
|
||||
from pixelle_video.utils.template_util import resolve_template_path
|
||||
from pixelle_video.utils.template_util import resolve_template_path, get_template_type
|
||||
|
||||
template_path = resolve_template_path(frame_template)
|
||||
generator = HTMLFrameGenerator(template_path)
|
||||
template_requires_image = generator.requires_image()
|
||||
template_name = Path(frame_template).name
|
||||
template_type = get_template_type(template_name)
|
||||
template_requires_image = (template_type == "image")
|
||||
|
||||
# Read media size from template meta tags
|
||||
template_path = resolve_template_path(frame_template)
|
||||
generator = HTMLFrameGenerator(template_path)
|
||||
image_width, image_height = generator.get_media_size()
|
||||
logger.info(f"📐 Media size from template: {image_width}x{image_height}")
|
||||
|
||||
if template_requires_image:
|
||||
if template_type == "image":
|
||||
logger.info(f"📸 Template requires image generation")
|
||||
else:
|
||||
logger.info(f"⚡ Template does not require images - skipping image generation pipeline")
|
||||
elif template_type == "video":
|
||||
logger.info(f"🎬 Template requires video generation")
|
||||
else: # static
|
||||
logger.info(f"⚡ Static template - skipping media generation pipeline")
|
||||
logger.info(f" 💡 Benefits: Faster generation + Lower cost + No ComfyUI dependency")
|
||||
|
||||
# ========== Step 1: Process content (CUSTOMIZE THIS) ==========
|
||||
@@ -197,8 +202,8 @@ class CustomPipeline(BasePipeline):
|
||||
# ========== Step 2: Generate image prompts (CONDITIONAL - CUSTOMIZE THIS) ==========
|
||||
self._report_progress(progress_callback, "generating_image_prompts", 0.25)
|
||||
|
||||
# IMPORTANT: Check if template actually needs images
|
||||
# If your template doesn't use {{image}}, you can skip this entire step!
|
||||
# IMPORTANT: Check if template is image type
|
||||
# If your template is static_*.html, you can skip this entire step!
|
||||
if template_requires_image:
|
||||
# Template requires images - generate image prompts using LLM
|
||||
from pixelle_video.utils.content_generators import generate_image_prompts
|
||||
|
||||
@@ -282,8 +282,8 @@ class StandardPipeline(BasePipeline):
|
||||
logger.info(f"🎬 Template requires video generation")
|
||||
elif template_media_type == "image":
|
||||
logger.info(f"📸 Template requires image generation")
|
||||
else: # text
|
||||
logger.info(f"⚡ Template does not require media - skipping media generation pipeline")
|
||||
else: # static
|
||||
logger.info(f"⚡ Static template - skipping media generation pipeline")
|
||||
logger.info(f" 💡 Benefits: Faster generation + Lower cost + No ComfyUI dependency")
|
||||
|
||||
try:
|
||||
@@ -525,35 +525,23 @@ class StandardPipeline(BasePipeline):
|
||||
- Media generation API calls
|
||||
- ComfyUI dependency
|
||||
|
||||
Template naming rules:
|
||||
Template naming convention:
|
||||
- static_*.html: Static style template (returns "static")
|
||||
- image_*.html: Image template (returns "image")
|
||||
- video_*.html: Video template (returns "video")
|
||||
- Other templates with {{image}}: Image template (returns "image")
|
||||
- Other templates without {{image}}: Text-only template (returns "text")
|
||||
|
||||
Args:
|
||||
frame_template: Template path (e.g., "1080x1920/default.html" or "1080x1920/video_default.html")
|
||||
frame_template: Template path (e.g., "1080x1920/image_default.html" or "1080x1920/video_default.html")
|
||||
|
||||
Returns:
|
||||
"video", "image", or "text"
|
||||
"static", "image", or "video"
|
||||
"""
|
||||
from pixelle_video.services.frame_html import HTMLFrameGenerator
|
||||
from pixelle_video.utils.template_util import resolve_template_path
|
||||
from pixelle_video.utils.template_util import get_template_type
|
||||
|
||||
# Check if template name starts with video_
|
||||
# Determine type by template filename prefix
|
||||
template_name = Path(frame_template).name
|
||||
if template_name.startswith("video_"):
|
||||
logger.debug(f"Template '{frame_template}' is video template (video_ prefix)")
|
||||
return "video"
|
||||
template_type = get_template_type(template_name)
|
||||
|
||||
# Check if template contains {{image}}
|
||||
template_path = resolve_template_path(frame_template)
|
||||
generator = HTMLFrameGenerator(template_path)
|
||||
|
||||
requires_image = generator.requires_image()
|
||||
if requires_image:
|
||||
logger.debug(f"Template '{frame_template}' is image template (has {{image}})")
|
||||
return "image"
|
||||
else:
|
||||
logger.debug(f"Template '{frame_template}' is text-only template")
|
||||
return "text"
|
||||
logger.debug(f"Template '{frame_template}' is {template_type} template")
|
||||
return template_type
|
||||
|
||||
|
||||
@@ -77,21 +77,6 @@ class HTMLFrameGenerator:
|
||||
self._check_linux_dependencies()
|
||||
logger.debug(f"Loaded HTML template: {template_path} (size: {self.width}x{self.height})")
|
||||
|
||||
def requires_image(self) -> bool:
|
||||
"""
|
||||
Detect if template requires {{image}} parameter
|
||||
|
||||
This method checks if the template uses the {{image}} variable.
|
||||
If the template doesn't use images, the entire image generation
|
||||
pipeline can be skipped, significantly improving:
|
||||
- Generation speed (no image generation API calls)
|
||||
- Cost efficiency (no LLM calls for image prompts)
|
||||
- Dependency requirements (no ComfyUI needed)
|
||||
|
||||
Returns:
|
||||
True if template contains {{image}}, False otherwise
|
||||
"""
|
||||
return '{{image}}' in self.template
|
||||
|
||||
def _check_linux_dependencies(self):
|
||||
"""Check Linux system dependencies and warn if missing"""
|
||||
|
||||
@@ -18,6 +18,7 @@ import os
|
||||
from pathlib import Path
|
||||
from typing import List, Tuple, Optional, Literal
|
||||
from pydantic import BaseModel, Field
|
||||
import logging
|
||||
|
||||
from pixelle_video.utils.os_util import (
|
||||
get_resource_path,
|
||||
@@ -26,6 +27,8 @@ from pixelle_video.utils.os_util import (
|
||||
resource_exists
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def parse_template_size(template_path: str) -> Tuple[int, int]:
|
||||
"""
|
||||
@@ -316,7 +319,7 @@ def resolve_template_path(template_input: Optional[str]) -> str:
|
||||
|
||||
Args:
|
||||
template_input: Can be:
|
||||
- None: Use default "1080x1920/default.html"
|
||||
- None: Use default "1080x1920/image_default.html"
|
||||
- "template.html": Use default size + this template
|
||||
- "1080x1920/template.html": Full relative path
|
||||
- "templates/1080x1920/template.html": Absolute-ish path (legacy)
|
||||
@@ -330,15 +333,15 @@ def resolve_template_path(template_input: Optional[str]) -> str:
|
||||
|
||||
Examples:
|
||||
>>> resolve_template_path(None)
|
||||
'templates/1080x1920/default.html'
|
||||
>>> resolve_template_path("modern.html")
|
||||
'templates/1080x1920/modern.html'
|
||||
>>> resolve_template_path("1920x1080/default.html")
|
||||
'templates/1920x1080/default.html'
|
||||
'templates/1080x1920/image_default.html'
|
||||
>>> resolve_template_path("image_modern.html")
|
||||
'templates/1080x1920/image_modern.html'
|
||||
>>> resolve_template_path("1920x1080/image_default.html")
|
||||
'templates/1920x1080/image_default.html'
|
||||
"""
|
||||
# Default case
|
||||
if template_input is None:
|
||||
template_input = "1080x1920/default.html"
|
||||
template_input = "1080x1920/image_default.html"
|
||||
|
||||
# Parse input to extract size and template name
|
||||
size = None
|
||||
@@ -359,6 +362,18 @@ def resolve_template_path(template_input: Optional[str]) -> str:
|
||||
size = "1080x1920"
|
||||
template_name = template_input
|
||||
|
||||
# Backward compatibility: migrate "default.html" to "image_default.html"
|
||||
if template_name == "default.html":
|
||||
migrated_name = "image_default.html"
|
||||
try:
|
||||
# Try migrated name first
|
||||
path = get_resource_path("templates", size, migrated_name)
|
||||
logger.info(f"Backward compatibility: migrated '{template_input}' to '{size}/{migrated_name}'")
|
||||
return path
|
||||
except FileNotFoundError:
|
||||
# Fall through to try original name
|
||||
logger.warning(f"Migrated template '{size}/{migrated_name}' not found, trying original name")
|
||||
|
||||
# Use resource API to resolve path (custom > default)
|
||||
try:
|
||||
return get_resource_path("templates", size, template_name)
|
||||
@@ -367,6 +382,120 @@ def resolve_template_path(template_input: Optional[str]) -> str:
|
||||
raise FileNotFoundError(
|
||||
f"Template not found: {size}/{template_name}\n"
|
||||
f"Available sizes: {available_sizes}\n"
|
||||
f"Hint: Use format 'SIZExSIZE/template.html' (e.g., '1080x1920/default.html')"
|
||||
f"Hint: Use format 'SIZExSIZE/template.html' (e.g., '1080x1920/image_default.html')"
|
||||
)
|
||||
|
||||
|
||||
def get_template_type(template_name: str) -> Literal['static', 'image', 'video']:
|
||||
"""
|
||||
Detect template type from template filename
|
||||
|
||||
Template naming convention:
|
||||
- static_*.html: Static style templates (no AI-generated media)
|
||||
- image_*.html: Templates requiring AI-generated images
|
||||
- video_*.html: Templates requiring AI-generated videos
|
||||
|
||||
Args:
|
||||
template_name: Template filename like "image_default.html" or "video_simple.html"
|
||||
|
||||
Returns:
|
||||
Template type: 'static', 'image', or 'video'
|
||||
|
||||
Examples:
|
||||
>>> get_template_type("static_simple.html")
|
||||
'static'
|
||||
>>> get_template_type("image_default.html")
|
||||
'image'
|
||||
>>> get_template_type("video_simple.html")
|
||||
'video'
|
||||
"""
|
||||
name = Path(template_name).name
|
||||
|
||||
if name.startswith("static_"):
|
||||
return "static"
|
||||
elif name.startswith("video_"):
|
||||
return "video"
|
||||
elif name.startswith("image_"):
|
||||
return "image"
|
||||
else:
|
||||
# Fallback: try to detect from legacy names
|
||||
logger.warning(
|
||||
f"Template '{template_name}' doesn't follow naming convention (static_/image_/video_). "
|
||||
f"Defaulting to 'image' type."
|
||||
)
|
||||
return "image"
|
||||
|
||||
|
||||
def filter_templates_by_type(
|
||||
templates: List[TemplateInfo],
|
||||
template_type: Literal['static', 'image', 'video']
|
||||
) -> List[TemplateInfo]:
|
||||
"""
|
||||
Filter templates by type
|
||||
|
||||
Args:
|
||||
templates: List of TemplateInfo objects
|
||||
template_type: Type to filter by ('static', 'image', or 'video')
|
||||
|
||||
Returns:
|
||||
Filtered list of TemplateInfo objects
|
||||
|
||||
Examples:
|
||||
>>> all_templates = get_all_templates_with_info()
|
||||
>>> image_templates = filter_templates_by_type(all_templates, 'image')
|
||||
>>> len(image_templates) > 0
|
||||
True
|
||||
"""
|
||||
filtered = []
|
||||
for t in templates:
|
||||
template_name = t.display_info.name
|
||||
if get_template_type(template_name) == template_type:
|
||||
filtered.append(t)
|
||||
return filtered
|
||||
|
||||
|
||||
def get_templates_grouped_by_size_and_type(
|
||||
template_type: Optional[Literal['static', 'image', 'video']] = None
|
||||
) -> dict:
|
||||
"""
|
||||
Get templates grouped by size, optionally filtered by type
|
||||
|
||||
Args:
|
||||
template_type: Optional type filter ('static', 'image', or 'video')
|
||||
|
||||
Returns:
|
||||
Dict with size as key, list of TemplateInfo as value
|
||||
Ordered by orientation priority: portrait > landscape > square
|
||||
|
||||
Examples:
|
||||
>>> # Get all templates
|
||||
>>> all_grouped = get_templates_grouped_by_size_and_type()
|
||||
|
||||
>>> # Get only image templates
|
||||
>>> image_grouped = get_templates_grouped_by_size_and_type('image')
|
||||
"""
|
||||
from collections import defaultdict
|
||||
|
||||
templates = get_all_templates_with_info()
|
||||
|
||||
# Filter by type if specified
|
||||
if template_type is not None:
|
||||
templates = filter_templates_by_type(templates, template_type)
|
||||
|
||||
grouped = defaultdict(list)
|
||||
|
||||
for t in templates:
|
||||
grouped[t.display_info.size].append(t)
|
||||
|
||||
# Sort groups by orientation priority: portrait > landscape > square
|
||||
orientation_priority = {'portrait': 0, 'landscape': 1, 'square': 2}
|
||||
|
||||
sorted_grouped = {}
|
||||
for size in sorted(grouped.keys(), key=lambda s: (
|
||||
orientation_priority.get(grouped[s][0].display_info.orientation, 3),
|
||||
s
|
||||
)):
|
||||
sorted_grouped[size] = sorted(grouped[size], key=lambda t: t.display_info.name)
|
||||
|
||||
return sorted_grouped
|
||||
|
||||
|
||||
Reference in New Issue
Block a user