Files
AI-Video/pixelle_video/pipelines/linear.py

162 lines
5.3 KiB
Python

# 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}")