diff --git a/config.example.yaml b/config.example.yaml index 1d12c89..0ecffec 100644 --- a/config.example.yaml +++ b/config.example.yaml @@ -37,3 +37,15 @@ comfyui: # Image prompt prefix (optional) prompt_prefix: "Minimalist black-and-white matchstick figure style illustration, clean lines, simple sketch style" + +# ==================== Template Configuration ==================== +# Configure default template for video generation +template: + # Default frame template to use when not explicitly specified + # Determines video aspect ratio and layout style + # Options: + # - 1080x1920 (vertical/portrait): default.html, modern.html, elegant.html, etc. + # - 1080x1080 (square): minimal_framed.html, magazine_cover.html, etc. + # - 1920x1080 (horizontal/landscape): film.html, full.html, etc. + # See templates/ directory for all available templates + default_template: "1080x1920/default.html" diff --git a/pixelle_video/config/manager.py b/pixelle_video/config/manager.py index 1ab98fc..597e160 100644 --- a/pixelle_video/config/manager.py +++ b/pixelle_video/config/manager.py @@ -35,7 +35,26 @@ class ConfigManager: def _load(self) -> PixelleVideoConfig: """Load configuration from file""" data = load_config_dict(str(self.config_path)) - return PixelleVideoConfig(**data) + config = PixelleVideoConfig(**data) + + # Validate template path exists + self._validate_template(config.template.default_template) + + return config + + def _validate_template(self, template_path: str): + """Validate that the configured template exists""" + from pixelle_video.utils.template_util import resolve_template_path + + try: + # Try to resolve the template path + resolved_path = resolve_template_path(template_path) + logger.debug(f"Template validation passed: {template_path} -> {resolved_path}") + except FileNotFoundError as e: + logger.warning( + f"Configured default template '{template_path}' not found. " + f"Will fall back to '1080x1920/default.html' if needed. Error: {e}" + ) def reload(self): """Reload configuration from file""" diff --git a/pixelle_video/config/schema.py b/pixelle_video/config/schema.py index 90423a1..cae6b99 100644 --- a/pixelle_video/config/schema.py +++ b/pixelle_video/config/schema.py @@ -55,11 +55,20 @@ class ComfyUIConfig(BaseModel): image: ImageSubConfig = Field(default_factory=ImageSubConfig, description="Image-specific configuration") +class TemplateConfig(BaseModel): + """Template configuration""" + default_template: str = Field( + default="1080x1920/default.html", + description="Default frame template path" + ) + + class PixelleVideoConfig(BaseModel): """Pixelle-Video main configuration""" project_name: str = Field(default="Pixelle-Video", description="Project name") llm: LLMConfig = Field(default_factory=LLMConfig) comfyui: ComfyUIConfig = Field(default_factory=ComfyUIConfig) + template: TemplateConfig = Field(default_factory=TemplateConfig) def is_llm_configured(self) -> bool: """Check if LLM is properly configured""" diff --git a/pixelle_video/pipelines/custom.py b/pixelle_video/pipelines/custom.py index 1724193..72302ad 100644 --- a/pixelle_video/pipelines/custom.py +++ b/pixelle_video/pipelines/custom.py @@ -83,7 +83,7 @@ class CustomPipeline(BasePipeline): image_width: int = 1024, image_height: int = 1024, - frame_template: str = "1080x1920/default.html", + frame_template: Optional[str] = None, video_fps: int = 30, output_path: Optional[str] = None, @@ -133,6 +133,12 @@ class CustomPipeline(BasePipeline): user_specified_output = output_path output_path = get_task_final_video_path(task_id) + # Determine frame template + # Priority: explicit param > config default > hardcoded default + if frame_template is None: + template_config = self.core.config.get("template", {}) + 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 diff --git a/pixelle_video/pipelines/standard.py b/pixelle_video/pipelines/standard.py index 0318dcc..72a88e4 100644 --- a/pixelle_video/pipelines/standard.py +++ b/pixelle_video/pipelines/standard.py @@ -221,6 +221,12 @@ class StandardPipeline(BasePipeline): else: # comfyui final_voice_id = voice_id # For ComfyUI, might be None + # Determine frame template + # Priority: explicit param > config default > hardcoded default + if frame_template is None: + template_config = self.core.config.get("template", {}) + frame_template = template_config.get("default_template", "1080x1920/default.html") + # Create storyboard config config = StoryboardConfig( task_id=task_id, @@ -238,7 +244,7 @@ class StandardPipeline(BasePipeline): image_width=image_width, image_height=image_height, image_workflow=image_workflow, - frame_template=frame_template or "1080x1920/default.html", + frame_template=frame_template, template_params=template_params ) diff --git a/web/app.py b/web/app.py index 37bd8e6..696f2be 100644 --- a/web/app.py +++ b/web/app.py @@ -670,6 +670,10 @@ def main(): default_index = 0 current_index = 0 + # Get default template from config + template_config = pixelle_video.config.get("template", {}) + config_default_template = template_config.get("default_template", "1080x1920/default.html") + for size, templates in grouped_templates.items(): if not templates: continue @@ -694,8 +698,10 @@ def main(): display_options.append(display_name) template_paths_ordered.append(t.template_path) # Add to ordered list - # Set default to first "default.html" in portrait orientation - if default_index == 0 and "default.html" in t.display_info.name and t.display_info.orientation == 'portrait': + # Set default based on config (priority: config > first default.html in portrait) + if t.template_path == config_default_template: + default_index = current_index + elif default_index == 0 and "default.html" in t.display_info.name and t.display_info.orientation == 'portrait': default_index = current_index current_index += 1