| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224 |
- # 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.
- """
- History Manager Service
- Business logic for history management (UI-agnostic).
- Provides high-level operations on top of PersistenceService.
- """
- from typing import List, Dict, Optional, Any
- from pathlib import Path
- from loguru import logger
- from pixelle_video.services.persistence import PersistenceService
- class HistoryManager:
- """
- History management service
-
- Provides business logic for:
- - Task listing and filtering
- - Task detail retrieval
- - Task duplication (for re-generation)
- - Task deletion
- - Future: Frame regeneration, export, etc.
- """
-
- def __init__(self, persistence: PersistenceService):
- """
- Initialize history manager
-
- Args:
- persistence: PersistenceService instance
- """
- self.persistence = persistence
-
- async def get_task_list(
- self,
- page: int = 1,
- page_size: int = 20,
- status: Optional[str] = None,
- sort_by: str = "created_at",
- sort_order: str = "desc"
- ) -> Dict[str, Any]:
- """
- Get paginated task list
-
- Args:
- page: Page number (1-indexed)
- page_size: Items per page
- status: Filter by status (optional)
- sort_by: Sort field (created_at, completed_at, title, duration)
- sort_order: Sort order (asc, desc)
-
- Returns:
- {
- "tasks": [...],
- "total": 100,
- "page": 1,
- "page_size": 20,
- "total_pages": 5
- }
- """
- return await self.persistence.list_tasks_paginated(
- page=page,
- page_size=page_size,
- status=status,
- sort_by=sort_by,
- sort_order=sort_order
- )
-
- async def get_task_detail(self, task_id: str) -> Optional[Dict[str, Any]]:
- """
- Get full task detail including storyboard
-
- Args:
- task_id: Task ID
-
- Returns:
- {
- "metadata": {...}, # Task metadata
- "storyboard": {...} # Storyboard data (if available)
- }
- or None if task not found
- """
- metadata = await self.persistence.load_task_metadata(task_id)
- if not metadata:
- return None
-
- storyboard = await self.persistence.load_storyboard(task_id)
-
- return {
- "metadata": metadata,
- "storyboard": storyboard,
- }
-
- async def get_statistics(self) -> Dict[str, Any]:
- """
- Get statistics about all tasks
-
- Returns:
- {
- "total_tasks": 100,
- "completed": 95,
- "failed": 5,
- "total_duration": 3600.5, # seconds
- "total_size": 1024000000, # bytes
- }
- """
- return await self.persistence.get_statistics()
-
- async def delete_task(self, task_id: str) -> bool:
- """
- Delete a task and all its files
-
- Args:
- task_id: Task ID to delete
-
- Returns:
- True if successful, False otherwise
- """
- return await self.persistence.delete_task(task_id)
-
- async def duplicate_task(self, task_id: str) -> Optional[Dict[str, Any]]:
- """
- Duplicate a task (get input parameters for new generation)
-
- This allows users to:
- 1. Copy all generation parameters from a previous task
- 2. Pre-fill the generation form
- 3. Regenerate with same/modified parameters
-
- Args:
- task_id: Task ID to duplicate
-
- Returns:
- Input parameters dict or None if task not found
- {
- "text": "...",
- "mode": "generate",
- "title": "...",
- "n_scenes": 5,
- "tts_inference_mode": "local",
- "tts_voice": "...",
- ...
- }
- """
- metadata = await self.persistence.load_task_metadata(task_id)
- if not metadata:
- logger.warning(f"Task {task_id} not found for duplication")
- return None
-
- # Extract input parameters
- input_params = metadata.get("input", {})
- logger.info(f"Duplicated task {task_id} parameters")
-
- return input_params
-
- async def rebuild_index(self):
- """Rebuild task index (useful for maintenance or after manual changes)"""
- await self.persistence.rebuild_index()
-
- # ========================================================================
- # Future Extensions (Phase 3)
- # ========================================================================
-
- async def regenerate_frame(
- self,
- task_id: str,
- frame_index: int,
- **override_params
- ) -> Optional[str]:
- """
- Regenerate a specific frame (FUTURE FEATURE)
-
- Args:
- task_id: Original task ID
- frame_index: Frame index to regenerate (0-based)
- **override_params: Parameters to override (image_prompt, style, etc.)
-
- Returns:
- New frame image path or None if failed
-
- TODO: Implement in Phase 3
- - Load original storyboard
- - Get frame parameters
- - Override with new parameters
- - Call image generation service
- - Update storyboard
- - Re-composite video
- """
- logger.warning("regenerate_frame is not implemented yet (Phase 3 feature)")
- return None
-
- async def export_task(self, task_id: str, export_path: str) -> Optional[str]:
- """
- Export task as a package (metadata + video + frames) (FUTURE FEATURE)
-
- Args:
- task_id: Task ID to export
- export_path: Export file path (e.g., "exports/task.zip")
-
- Returns:
- Export file path or None if failed
-
- TODO: Implement in Phase 3
- - Collect all task files
- - Create ZIP archive
- - Include metadata.json, storyboard.json, video, frames
- """
- logger.warning("export_task is not implemented yet (Phase 3 feature)")
- return None
|