linear.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. # Copyright (C) 2025 AIDC-AI
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. # http://www.apache.org/licenses/LICENSE-2.0
  7. # Unless required by applicable law or agreed to in writing, software
  8. # distributed under the License is distributed on an "AS IS" BASIS,
  9. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. # See the License for the specific language governing permissions and
  11. # limitations under the License.
  12. """
  13. Linear Video Pipeline Base Class
  14. This module defines the template method pattern for linear video generation workflows.
  15. It introduces `PipelineContext` for state management and `LinearVideoPipeline` for
  16. process orchestration.
  17. """
  18. from dataclasses import dataclass, field
  19. from typing import Optional, List, Dict, Any, Callable
  20. from loguru import logger
  21. from pixelle_video.pipelines.base import BasePipeline
  22. from pixelle_video.models.storyboard import (
  23. Storyboard,
  24. VideoGenerationResult,
  25. StoryboardConfig
  26. )
  27. from pixelle_video.models.progress import ProgressEvent
  28. @dataclass
  29. class PipelineContext:
  30. """
  31. Context object holding the state of a single pipeline execution.
  32. This object is passed between steps in the LinearVideoPipeline lifecycle.
  33. """
  34. # === Input ===
  35. input_text: str
  36. params: Dict[str, Any]
  37. progress_callback: Optional[Callable[[ProgressEvent], None]] = None
  38. # === Task State ===
  39. task_id: Optional[str] = None
  40. task_dir: Optional[str] = None
  41. # === Content ===
  42. title: Optional[str] = None
  43. narrations: List[str] = field(default_factory=list)
  44. # === Visuals ===
  45. image_prompts: List[Optional[str]] = field(default_factory=list)
  46. # === Configuration & Storyboard ===
  47. config: Optional[StoryboardConfig] = None
  48. storyboard: Optional[Storyboard] = None
  49. # === Output ===
  50. final_video_path: Optional[str] = None
  51. result: Optional[VideoGenerationResult] = None
  52. class LinearVideoPipeline(BasePipeline):
  53. """
  54. Base class for linear video generation pipelines using the Template Method pattern.
  55. This class orchestrates the video generation process into distinct lifecycle steps:
  56. 1. setup_environment
  57. 2. generate_content
  58. 3. determine_title
  59. 4. plan_visuals
  60. 5. initialize_storyboard
  61. 6. produce_assets
  62. 7. post_production
  63. 8. finalize
  64. Subclasses should override specific steps to customize behavior while maintaining
  65. the overall workflow structure.
  66. """
  67. async def __call__(
  68. self,
  69. text: str,
  70. progress_callback: Optional[Callable[[ProgressEvent], None]] = None,
  71. **kwargs
  72. ) -> VideoGenerationResult:
  73. """
  74. Execute the pipeline using the template method.
  75. """
  76. # 1. Initialize context
  77. ctx = PipelineContext(
  78. input_text=text,
  79. params=kwargs,
  80. progress_callback=progress_callback
  81. )
  82. try:
  83. # === Phase 1: Preparation ===
  84. await self.setup_environment(ctx)
  85. # === Phase 2: Content Creation ===
  86. await self.generate_content(ctx)
  87. await self.determine_title(ctx)
  88. # === Phase 3: Visual Planning ===
  89. await self.plan_visuals(ctx)
  90. await self.initialize_storyboard(ctx)
  91. # === Phase 4: Asset Production ===
  92. await self.produce_assets(ctx)
  93. # === Phase 5: Post Production ===
  94. await self.post_production(ctx)
  95. # === Phase 6: Finalization ===
  96. return await self.finalize(ctx)
  97. except Exception as e:
  98. await self.handle_exception(ctx, e)
  99. raise
  100. # ==================== Lifecycle Methods ====================
  101. async def setup_environment(self, ctx: PipelineContext):
  102. """Step 1: Setup task directory and environment."""
  103. pass
  104. async def generate_content(self, ctx: PipelineContext):
  105. """Step 2: Generate or process script/narrations."""
  106. pass
  107. async def determine_title(self, ctx: PipelineContext):
  108. """Step 3: Determine or generate video title."""
  109. pass
  110. async def plan_visuals(self, ctx: PipelineContext):
  111. """Step 4: Generate image prompts or visual descriptions."""
  112. pass
  113. async def initialize_storyboard(self, ctx: PipelineContext):
  114. """Step 5: Create Storyboard object and frames."""
  115. pass
  116. async def produce_assets(self, ctx: PipelineContext):
  117. """Step 6: Generate audio, images, and render frames (Core processing)."""
  118. pass
  119. async def post_production(self, ctx: PipelineContext):
  120. """Step 7: Concatenate videos and add BGM."""
  121. pass
  122. async def finalize(self, ctx: PipelineContext) -> VideoGenerationResult:
  123. """Step 8: Create result object and persist metadata."""
  124. raise NotImplementedError("finalize must be implemented by subclass")
  125. async def handle_exception(self, ctx: PipelineContext, error: Exception):
  126. """Handle exceptions during pipeline execution."""
  127. logger.error(f"Pipeline execution failed: {error}")