# Copyright (C) 2025 AIDC-AI # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ Linear Video Pipeline Base Class This module defines the template method pattern for linear video generation workflows. It introduces `PipelineContext` for state management and `LinearVideoPipeline` for process orchestration. """ from dataclasses import dataclass, field from typing import Optional, List, Dict, Any, Callable from loguru import logger from pixelle_video.pipelines.base import BasePipeline from pixelle_video.models.storyboard import ( Storyboard, VideoGenerationResult, StoryboardConfig ) from pixelle_video.models.progress import ProgressEvent @dataclass class PipelineContext: """ Context object holding the state of a single pipeline execution. This object is passed between steps in the LinearVideoPipeline lifecycle. """ # === Input === input_text: str params: Dict[str, Any] progress_callback: Optional[Callable[[ProgressEvent], None]] = None # === Task State === task_id: Optional[str] = None task_dir: Optional[str] = None # === Content === title: Optional[str] = None narrations: List[str] = field(default_factory=list) # === Visuals === image_prompts: List[Optional[str]] = field(default_factory=list) # === Configuration & Storyboard === config: Optional[StoryboardConfig] = None storyboard: Optional[Storyboard] = None # === Output === final_video_path: Optional[str] = None result: Optional[VideoGenerationResult] = None class LinearVideoPipeline(BasePipeline): """ Base class for linear video generation pipelines using the Template Method pattern. This class orchestrates the video generation process into distinct lifecycle steps: 1. setup_environment 2. generate_content 3. determine_title 4. plan_visuals 5. initialize_storyboard 6. produce_assets 7. post_production 8. finalize Subclasses should override specific steps to customize behavior while maintaining the overall workflow structure. """ async def __call__( self, text: str, progress_callback: Optional[Callable[[ProgressEvent], None]] = None, **kwargs ) -> VideoGenerationResult: """ Execute the pipeline using the template method. """ # 1. Initialize context ctx = PipelineContext( input_text=text, params=kwargs, progress_callback=progress_callback ) try: # === Phase 1: Preparation === await self.setup_environment(ctx) # === Phase 2: Content Creation === await self.generate_content(ctx) await self.determine_title(ctx) # === Phase 3: Visual Planning === await self.plan_visuals(ctx) await self.initialize_storyboard(ctx) # === Phase 4: Asset Production === await self.produce_assets(ctx) # === Phase 5: Post Production === await self.post_production(ctx) # === Phase 6: Finalization === return await self.finalize(ctx) except Exception as e: await self.handle_exception(ctx, e) raise # ==================== Lifecycle Methods ==================== async def setup_environment(self, ctx: PipelineContext): """Step 1: Setup task directory and environment.""" pass async def generate_content(self, ctx: PipelineContext): """Step 2: Generate or process script/narrations.""" pass async def determine_title(self, ctx: PipelineContext): """Step 3: Determine or generate video title.""" pass async def plan_visuals(self, ctx: PipelineContext): """Step 4: Generate image prompts or visual descriptions.""" pass async def initialize_storyboard(self, ctx: PipelineContext): """Step 5: Create Storyboard object and frames.""" pass async def produce_assets(self, ctx: PipelineContext): """Step 6: Generate audio, images, and render frames (Core processing).""" pass async def post_production(self, ctx: PipelineContext): """Step 7: Concatenate videos and add BGM.""" pass async def finalize(self, ctx: PipelineContext) -> VideoGenerationResult: """Step 8: Create result object and persist metadata.""" raise NotImplementedError("finalize must be implemented by subclass") async def handle_exception(self, ctx: PipelineContext, error: Exception): """Handle exceptions during pipeline execution.""" logger.error(f"Pipeline execution failed: {error}")