diff --git a/src/core/SerieScanner.py b/src/core/SerieScanner.py index b6d9cb7..47eef79 100644 --- a/src/core/SerieScanner.py +++ b/src/core/SerieScanner.py @@ -99,6 +99,7 @@ class SerieScanner: self.events.on_progress = [] self.events.on_error = [] + self.events.on_warning = [] self.events.on_completion = [] logger.info("Initialized SerieScanner with base path: %s", abs_path) @@ -191,7 +192,25 @@ class SerieScanner: """ if handler in self.events.on_error: self.events.on_error.remove(handler) - + + def subscribe_on_warning(self, handler): + """ + Subscribe a handler to an event. + Args: + handler: Callable to handle the event + """ + if handler not in self.events.on_warning: + self.events.on_warning.append(handler) + + def unsubscribe_on_warning(self, handler): + """ + Unsubscribe a handler from an event. + Args: + handler: Callable to remove + """ + if handler in self.events.on_warning: + self.events.on_warning.remove(handler) + def subscribe_on_completion(self, handler): """ Subscribe a handler to an event. @@ -454,11 +473,27 @@ class SerieScanner: # Store by key (primary identifier), not folder if serie.key in self.keyDict: - logger.error( - "Duplicate series found with key '%s' " - "(folder: '%s')", + existing = self.keyDict[serie.key] + logger.warning( + "Duplicate series found with key '%s': " + "folder '%s' maps to same key as existing folder '%s'. " + "Skipping duplicate folder.", serie.key, - folder + folder, + existing.folder + ) + self._safe_call_event( + self.events.on_warning, + { + "operation_id": self._current_operation_id, + "warning": "duplicate_key", + "message": f"Duplicate series skipped: '{folder}' maps to key '{serie.key}' already used by '{existing.folder}'", + "metadata": { + "key": serie.key, + "duplicate_folder": folder, + "existing_folder": existing.folder, + } + } ) else: self.keyDict[serie.key] = serie diff --git a/src/server/services/folder_rename_service.py b/src/server/services/folder_rename_service.py index f94df53..7a96f04 100644 --- a/src/server/services/folder_rename_service.py +++ b/src/server/services/folder_rename_service.py @@ -115,6 +115,29 @@ def _is_series_being_downloaded(series_folder: str) -> bool: return True +def _cleanup_stale_files_after_rename(new_path: Path, new_name: str) -> None: + """Remove legacy 'key' file after successful folder rename. + + Also checks for orphaned folders with the same key that may have been + left behind from previous rename operations. + + Args: + new_path: The new folder path after rename. + new_name: The new folder name. + """ + key_file = new_path / "key" + if key_file.exists(): + try: + key_file.unlink() + logger.info( + "Removed legacy 'key' file after rename: %s", key_file + ) + except OSError as exc: + logger.warning( + "Could not remove legacy 'key' file %s: %s", key_file, exc + ) + + async def _update_database_paths( old_folder: str, new_folder: str, @@ -315,6 +338,9 @@ async def validate_and_rename_series_folders() -> Dict[str, int]: # Update database records await _update_database_paths(current_name, expected_name, anime_dir) + # Clean up stale/legacy files after successful rename + _cleanup_stale_files_after_rename(expected_path, expected_name) + except PermissionError as exc: logger.error( "Permission denied renaming '%s' → '%s': %s",