| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165 |
- # 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.
- """
- Lightweight batch manager for Streamlit (Simplified YAGNI version)
- """
- import time
- import traceback
- from typing import List, Dict, Any, Optional, Callable
- from loguru import logger
- class SimpleBatchManager:
- """
- Ultra-simple batch manager following YAGNI principle
-
- Design principles:
- 1. Only supports "AI generate content" mode
- 2. Same config for all videos, only topics differ
- 3. No CSV, no complex validation, just loop and execute
- """
-
- def __init__(self):
- self.results = []
- self.errors = []
- self.current_index = 0
- self.total_count = 0
-
- def execute_batch(
- self,
- pixelle_video,
- topics: List[str],
- shared_config: Dict[str, Any],
- overall_progress_callback: Optional[Callable] = None,
- task_progress_callback_factory: Optional[Callable] = None
- ) -> Dict[str, Any]:
- """
- Execute batch generation with shared config
-
- Args:
- pixelle_video: PixelleVideoCore instance
- topics: List of topics (one per video)
- shared_config: Shared configuration for all videos
- overall_progress_callback: Callback for overall progress
- task_progress_callback_factory: Factory function to create per-task callback
-
- Returns:
- {
- "results": [...],
- "errors": [...],
- "total_count": N,
- "success_count": M,
- "failed_count": K
- }
- """
- self.results = []
- self.errors = []
- self.total_count = len(topics)
-
- logger.info(f"Starting batch generation: {self.total_count} topics")
-
- for idx, topic in enumerate(topics, 1):
- self.current_index = idx
-
- # Report overall progress
- if overall_progress_callback:
- overall_progress_callback(
- current=idx,
- total=self.total_count,
- topic=topic
- )
-
- try:
- logger.info(f"Task {idx}/{self.total_count} started: {topic}")
-
- # Extract title_prefix from shared_config (not a valid parameter for generate_video)
- title_prefix = shared_config.get("title_prefix")
-
- # Build task params (merge topic with shared config, excluding title_prefix)
- task_params = {
- "text": topic, # Topic as input
- "mode": "generate", # Fixed mode
- }
-
- # Merge shared config, excluding title_prefix and None values
- # Filter out None values to avoid interfering with parameter logic in generate_video
- for key, value in shared_config.items():
- if key != "title_prefix" and value is not None:
- task_params[key] = value
-
- # Generate title using title_prefix
- if title_prefix:
- task_params["title"] = f"{title_prefix} - {topic}"
- else:
- # Use topic as title
- task_params["title"] = topic
-
- # Add per-task progress callback
- if task_progress_callback_factory:
- task_params["progress_callback"] = task_progress_callback_factory(idx, topic)
-
- # Execute generation
- from web.utils.async_helpers import run_async
- result = run_async(pixelle_video.generate_video(**task_params))
-
- # Extract task_id from video_path (e.g., output/20251118_173821_f96a/final.mp4)
- from pathlib import Path
- task_id = Path(result.video_path).parent.name
-
- # Record success
- self.results.append({
- "index": idx,
- "topic": topic,
- "task_id": task_id,
- "video_path": result.video_path,
- "status": "success"
- })
-
- logger.info(f"Task {idx}/{self.total_count} completed: {result.video_path}")
-
- except Exception as e:
- # Record error but continue
- error_msg = str(e)
- error_trace = traceback.format_exc()
-
- logger.error(f"Task {idx}/{self.total_count} failed: {error_msg}")
- logger.debug(f"Error traceback:\n{error_trace}")
-
- self.errors.append({
- "index": idx,
- "topic": topic,
- "error": error_msg,
- "traceback": error_trace,
- "status": "failed"
- })
-
- # Continue to next task
- continue
-
- success_count = len(self.results)
- failed_count = len(self.errors)
-
- logger.info(
- f"Batch generation completed: "
- f"{success_count}/{self.total_count} succeeded, "
- f"{failed_count} failed"
- )
-
- return {
- "results": self.results,
- "errors": self.errors,
- "total_count": self.total_count,
- "success_count": success_count,
- "failed_count": failed_count
- }
|