history_manager.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  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. History Manager Service
  14. Business logic for history management (UI-agnostic).
  15. Provides high-level operations on top of PersistenceService.
  16. """
  17. from typing import List, Dict, Optional, Any
  18. from pathlib import Path
  19. from loguru import logger
  20. from pixelle_video.services.persistence import PersistenceService
  21. class HistoryManager:
  22. """
  23. History management service
  24. Provides business logic for:
  25. - Task listing and filtering
  26. - Task detail retrieval
  27. - Task duplication (for re-generation)
  28. - Task deletion
  29. - Future: Frame regeneration, export, etc.
  30. """
  31. def __init__(self, persistence: PersistenceService):
  32. """
  33. Initialize history manager
  34. Args:
  35. persistence: PersistenceService instance
  36. """
  37. self.persistence = persistence
  38. async def get_task_list(
  39. self,
  40. page: int = 1,
  41. page_size: int = 20,
  42. status: Optional[str] = None,
  43. sort_by: str = "created_at",
  44. sort_order: str = "desc"
  45. ) -> Dict[str, Any]:
  46. """
  47. Get paginated task list
  48. Args:
  49. page: Page number (1-indexed)
  50. page_size: Items per page
  51. status: Filter by status (optional)
  52. sort_by: Sort field (created_at, completed_at, title, duration)
  53. sort_order: Sort order (asc, desc)
  54. Returns:
  55. {
  56. "tasks": [...],
  57. "total": 100,
  58. "page": 1,
  59. "page_size": 20,
  60. "total_pages": 5
  61. }
  62. """
  63. return await self.persistence.list_tasks_paginated(
  64. page=page,
  65. page_size=page_size,
  66. status=status,
  67. sort_by=sort_by,
  68. sort_order=sort_order
  69. )
  70. async def get_task_detail(self, task_id: str) -> Optional[Dict[str, Any]]:
  71. """
  72. Get full task detail including storyboard
  73. Args:
  74. task_id: Task ID
  75. Returns:
  76. {
  77. "metadata": {...}, # Task metadata
  78. "storyboard": {...} # Storyboard data (if available)
  79. }
  80. or None if task not found
  81. """
  82. metadata = await self.persistence.load_task_metadata(task_id)
  83. if not metadata:
  84. return None
  85. storyboard = await self.persistence.load_storyboard(task_id)
  86. return {
  87. "metadata": metadata,
  88. "storyboard": storyboard,
  89. }
  90. async def get_statistics(self) -> Dict[str, Any]:
  91. """
  92. Get statistics about all tasks
  93. Returns:
  94. {
  95. "total_tasks": 100,
  96. "completed": 95,
  97. "failed": 5,
  98. "total_duration": 3600.5, # seconds
  99. "total_size": 1024000000, # bytes
  100. }
  101. """
  102. return await self.persistence.get_statistics()
  103. async def delete_task(self, task_id: str) -> bool:
  104. """
  105. Delete a task and all its files
  106. Args:
  107. task_id: Task ID to delete
  108. Returns:
  109. True if successful, False otherwise
  110. """
  111. return await self.persistence.delete_task(task_id)
  112. async def duplicate_task(self, task_id: str) -> Optional[Dict[str, Any]]:
  113. """
  114. Duplicate a task (get input parameters for new generation)
  115. This allows users to:
  116. 1. Copy all generation parameters from a previous task
  117. 2. Pre-fill the generation form
  118. 3. Regenerate with same/modified parameters
  119. Args:
  120. task_id: Task ID to duplicate
  121. Returns:
  122. Input parameters dict or None if task not found
  123. {
  124. "text": "...",
  125. "mode": "generate",
  126. "title": "...",
  127. "n_scenes": 5,
  128. "tts_inference_mode": "local",
  129. "tts_voice": "...",
  130. ...
  131. }
  132. """
  133. metadata = await self.persistence.load_task_metadata(task_id)
  134. if not metadata:
  135. logger.warning(f"Task {task_id} not found for duplication")
  136. return None
  137. # Extract input parameters
  138. input_params = metadata.get("input", {})
  139. logger.info(f"Duplicated task {task_id} parameters")
  140. return input_params
  141. async def rebuild_index(self):
  142. """Rebuild task index (useful for maintenance or after manual changes)"""
  143. await self.persistence.rebuild_index()
  144. # ========================================================================
  145. # Future Extensions (Phase 3)
  146. # ========================================================================
  147. async def regenerate_frame(
  148. self,
  149. task_id: str,
  150. frame_index: int,
  151. **override_params
  152. ) -> Optional[str]:
  153. """
  154. Regenerate a specific frame (FUTURE FEATURE)
  155. Args:
  156. task_id: Original task ID
  157. frame_index: Frame index to regenerate (0-based)
  158. **override_params: Parameters to override (image_prompt, style, etc.)
  159. Returns:
  160. New frame image path or None if failed
  161. TODO: Implement in Phase 3
  162. - Load original storyboard
  163. - Get frame parameters
  164. - Override with new parameters
  165. - Call image generation service
  166. - Update storyboard
  167. - Re-composite video
  168. """
  169. logger.warning("regenerate_frame is not implemented yet (Phase 3 feature)")
  170. return None
  171. async def export_task(self, task_id: str, export_path: str) -> Optional[str]:
  172. """
  173. Export task as a package (metadata + video + frames) (FUTURE FEATURE)
  174. Args:
  175. task_id: Task ID to export
  176. export_path: Export file path (e.g., "exports/task.zip")
  177. Returns:
  178. Export file path or None if failed
  179. TODO: Implement in Phase 3
  180. - Collect all task files
  181. - Create ZIP archive
  182. - Include metadata.json, storyboard.json, video, frames
  183. """
  184. logger.warning("export_task is not implemented yet (Phase 3 feature)")
  185. return None