feat: improve batch task controls and release 1.2.0

This commit is contained in:
chenmingxuan committed 2026-09-25 02:02:48 +08:00
1 parent 0e13bb5c29
commit 84e183d28d
38 files changed
+632 -147

No files matched your search

+40 -38
View File
@@ -1158,11 +1158,21 @@ class StorageManager:
"""Delete all data associated with a session, including files on disk."""
session_id_str = str(session_id)
# 1. Get video paths before deleting from DB
# 1. Get files referenced by this session before deleting from DB.
video_paths = []
video_ids: list[str] = []
image_names: set[str] = set()
try:
with self._get_connection() as conn:
image_names = {
str(name)
for row in conn.execute(
"SELECT pre_image_name, post_image_name FROM steps WHERE session_id = ?",
(session_id_str,),
)
for name in row
if name
}
cursor = conn.execute(
"SELECT video_id, local_video_path FROM video_recordings WHERE session_id = ?",
(session_id_str,),
@@ -1173,9 +1183,11 @@ class StorageManager:
if row["local_video_path"]:
video_paths.append(Path(row["local_video_path"]))
except sqlite3.OperationalError as e:
logger.warning(f"Could not query video_recordings: {e}")
logger.warning(f"Could not query session media: {e}")
raise
# 2. Delete from DB tables
orphaned_images: list[str] = []
with self._get_connection() as conn:
conn.execute("PRAGMA foreign_keys = OFF")
board_keys = [f"session:{session_id_str}"] + [
@@ -1207,13 +1219,26 @@ class StorageManager:
(session_id_str,),
)
except sqlite3.OperationalError as e:
logger.warning(f"Failed to delete from table {table}: {e}")
conn.commit()
conn.execute("VACUUM")
logger.error(f"Failed to delete from table {table}: {e}")
raise
for name in image_names:
if not conn.execute(
"SELECT 1 FROM steps WHERE pre_image_name=? OR post_image_name=? LIMIT 1",
(name, name),
).fetchone():
conn.execute("DELETE FROM images WHERE image_name=?", (name,))
orphaned_images.append(name)
conn.commit()
logger.info(f"Database records for session {session_id} cleared.")
images_root = (self.base_trace_dir / "images").resolve()
for name in orphaned_images:
if len(name) == 64 and all(c in "0123456789abcdef" for c in name):
image_path = (images_root / f"{name}.jpg").resolve()
if image_path.is_relative_to(images_root):
image_path.unlink(missing_ok=True)
# 3. Delete session directory (notes, etc.)
session_dir = self.base_trace_dir / session_id_str
if session_dir.exists() and session_dir.is_dir():
@@ -1222,38 +1247,15 @@ class StorageManager:
logger.info(f"Deleted session directory: {session_dir}")
except Exception as e:
logger.error(f"Failed to delete session directory {session_dir}: {e}")
raise
# 4. Delete video files and their containing folders (which might have been renamed)
# 4. Never let a persisted recording path remove a sibling session.
trace_root = self.base_trace_dir.resolve()
for video_path in video_paths:
# video_path is likely: /path/to/traces/task_name/recording.mp4
# If it was renamed, it might be: /path/to/traces/task_name_PASS_timestamp/recording.mp4
task_name = video_path.parent.name
if task_name and task_name != "traces" and task_name != "..":
# Search for directories starting with task_name in base_trace_dir
try:
for path in self.base_trace_dir.iterdir():
if path.is_dir() and path.name.startswith(task_name):
try:
shutil.rmtree(path)
logger.info(f"Deleted trace/video directory: {path}")
except Exception as e:
logger.error(f"Failed to delete directory {path}: {e}")
elif path.is_file() and path.name.startswith(task_name):
try:
path.unlink()
logger.info(f"Deleted trace/video file: {path}")
except Exception as e:
logger.error(f"Failed to delete file {path}: {e}")
except Exception as e:
logger.error(f"Error iterating base_trace_dir for cleanup: {e}")
if video_path.exists():
try:
video_path.unlink()
logger.info(f"Deleted video file directly: {video_path}")
if video_path.parent.exists() and not any(video_path.parent.iterdir()):
video_path.parent.rmdir()
except Exception as e:
logger.error(f"Failed to delete video file directly {video_path}: {e}")
resolved = video_path.resolve()
if not resolved.is_relative_to(trace_root):
continue
relative = resolved.relative_to(trace_root)
if not relative.parts or relative.parts[0] != session_id_str:
continue
resolved.unlink(missing_ok=True)