From 0efc6e153f59a563f910f4f8816d462e9fa46535 Mon Sep 17 00:00:00 2001 From: Ethanfel Date: Thu, 12 Feb 2026 12:06:01 +0100 Subject: [PATCH] Fix session save freeze and restore losing removed files Save freeze fix: - Added record_symlinks_batch() that inserts all symlinks in a single DB transaction instead of opening a new connection per file - _save_session and _auto_save_session now use batch inserts - With 1700 files this goes from 1700 connection cycles to 1 Removed files fix: - _restore_files_from_session now filters by _removed_files so individually deleted files stay removed even when restoring from session data that pre-dates the removal Co-Authored-By: Claude Opus 4.6 --- core/database.py | 25 +++++++++++++++++++++++++ ui/main_window.py | 39 ++++++++++++++++++++++----------------- 2 files changed, 47 insertions(+), 17 deletions(-) diff --git a/core/database.py b/core/database.py index 6b46583..750cd91 100644 --- a/core/database.py +++ b/core/database.py @@ -216,6 +216,31 @@ class DatabaseManager: except sqlite3.Error as e: raise DatabaseError(f"Failed to record symlink: {e}") from e + def record_symlinks_batch( + self, + session_id: int, + records: list[tuple[str, str, str, int]], + ) -> None: + """Record multiple symlinks in a single transaction. + + Args: + session_id: The session these symlinks belong to. + records: List of (source, link, filename, seq) tuples. + + Raises: + DatabaseError: If recording fails. + """ + try: + with self._connect() as conn: + conn.executemany( + """INSERT INTO symlinks + (session_id, source_path, link_path, original_filename, sequence_number) + VALUES (?, ?, ?, ?, ?)""", + [(session_id, src, lnk, fname, seq) for src, lnk, fname, seq in records] + ) + except sqlite3.Error as e: + raise DatabaseError(f"Failed to record symlinks: {e}") from e + def get_sessions(self) -> list[SessionRecord]: """List all sessions with link counts. diff --git a/ui/main_window.py b/ui/main_window.py index e208843..6a79532 100644 --- a/ui/main_window.py +++ b/ui/main_window.py @@ -3166,17 +3166,18 @@ class SequenceLinkerUI(QWidget): self._save_session_settings(session_id, save_effective_types=True) # Save the file list so the exact sequence can be restored + records = [] for i, (source_dir, filename, folder_idx, file_idx) in enumerate(files): source_path = source_dir / filename ext = source_path.suffix link_name = f"seq{folder_idx + 1:02d}_{file_idx:04d}{ext}" - self.db.record_symlink( - session_id=session_id, - source=str(source_path.resolve()), - link=str(Path(dest) / link_name), - filename=filename, - seq=i, - ) + records.append(( + str(source_path.resolve()), + str(Path(dest) / link_name), + filename, + i, + )) + self.db.record_symlinks_batch(session_id, records) except Exception: pass # Best-effort save on close @@ -3200,19 +3201,20 @@ class SequenceLinkerUI(QWidget): self._save_session_settings(session_id, save_effective_types=True) - # Save the exact file list + # Save the exact file list in a single transaction files = self._get_files_in_order() + records = [] for i, (source_dir, filename, folder_idx, file_idx) in enumerate(files): source_path = source_dir / filename ext = source_path.suffix link_name = f"seq{folder_idx + 1:02d}_{file_idx:04d}{ext}" - self.db.record_symlink( - session_id=session_id, - source=str(source_path.resolve()), - link=str(Path(dest) / link_name), - filename=filename, - seq=i, - ) + records.append(( + str(source_path.resolve()), + str(Path(dest) / link_name), + filename, + i, + )) + self.db.record_symlinks_batch(session_id, records) main_count = sum( 1 for i, f in enumerate(self.source_folders) @@ -3667,8 +3669,11 @@ class SequenceLinkerUI(QWidget): # Sort files by their sequence index sorted_files = sorted(file_list, key=lambda x: x[0]) - # Folder existence already verified in _try_resume_session; - # only recheck individual files if the folder has changed on disk. + # Filter out individually removed files + removed = self._removed_files.get(folder_path, set()) + if removed: + sorted_files = [(idx, fname) for idx, fname in sorted_files if fname not in removed] + if not sorted_files: continue