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