diff --git a/src/core/SeriesApp.py b/src/core/SeriesApp.py index 599ce0e..93c7d29 100644 --- a/src/core/SeriesApp.py +++ b/src/core/SeriesApp.py @@ -457,14 +457,27 @@ class SeriesApp: return False - async def rescan(self) -> int: + async def rescan(self, use_database: bool = True) -> int: """ Rescan directory for missing episodes (async). + + When use_database is True (default), scan results are saved to the + database instead of data files. This is the preferred mode for the + web application. + + Args: + use_database: If True, save scan results to database. + If False, use legacy file-based storage (deprecated). Returns: Number of series with missing episodes after rescan. """ - logger.info("Starting directory rescan") + logger.info( + "Starting directory rescan (database mode: %s)", + use_database + ) + + total_to_scan = 0 try: # Get total items to scan @@ -507,12 +520,34 @@ class SeriesApp: ) ) - # Perform scan - await asyncio.to_thread(self.serie_scanner.scan, scan_callback) + # Perform scan - use database mode when available + if use_database: + # Import here to avoid circular imports and allow CLI usage + # without database dependencies + from src.server.database.connection import get_db_session + + async with get_db_session() as db: + await self.serie_scanner.scan_async(db, scan_callback) + logger.info("Scan results saved to database") + else: + # Legacy file-based mode (deprecated) + await asyncio.to_thread( + self.serie_scanner.scan, scan_callback + ) - # Reinitialize list - self.list = SerieList(self.directory_to_search) - await self._init_list() + # Reinitialize list from the appropriate source + if use_database: + from src.server.database.connection import get_db_session + + async with get_db_session() as db: + self.list = SerieList( + self.directory_to_search, db_session=db + ) + await self.list.load_series_from_db(db) + self.series_list = self.list.GetMissingEpisode() + else: + self.list = SerieList(self.directory_to_search) + await self._init_list() logger.info("Directory rescan completed successfully") @@ -540,7 +575,7 @@ class SeriesApp: self._events.scan_status( ScanStatusEventArgs( current=0, - total=total_to_scan if 'total_to_scan' in locals() else 0, + total=total_to_scan, folder="", status="cancelled", message="Scan cancelled by user", @@ -555,7 +590,7 @@ class SeriesApp: self._events.scan_status( ScanStatusEventArgs( current=0, - total=total_to_scan if 'total_to_scan' in locals() else 0, + total=total_to_scan, folder="", status="failed", error=e, diff --git a/tests/unit/test_series_app.py b/tests/unit/test_series_app.py index 90b246b..6e5b47f 100644 --- a/tests/unit/test_series_app.py +++ b/tests/unit/test_series_app.py @@ -240,7 +240,7 @@ class TestSeriesAppReScan: async def test_rescan_success( self, mock_serie_list, mock_scanner, mock_loaders ): - """Test successful directory rescan.""" + """Test successful directory rescan (file-based mode).""" test_dir = "/test/anime" app = SeriesApp(test_dir) @@ -252,8 +252,8 @@ class TestSeriesAppReScan: app.serie_scanner.reinit = Mock() app.serie_scanner.scan = Mock() - # Perform rescan - await app.rescan() + # Perform rescan with file-based mode (use_database=False) + await app.rescan(use_database=False) # Verify rescan completed app.serie_scanner.reinit.assert_called_once() @@ -266,7 +266,7 @@ class TestSeriesAppReScan: async def test_rescan_with_callback( self, mock_serie_list, mock_scanner, mock_loaders ): - """Test rescan with progress callbacks.""" + """Test rescan with progress callbacks (file-based mode).""" test_dir = "/test/anime" app = SeriesApp(test_dir) @@ -284,8 +284,8 @@ class TestSeriesAppReScan: app.serie_scanner.scan = Mock(side_effect=mock_scan) - # Perform rescan - await app.rescan() + # Perform rescan with file-based mode (use_database=False) + await app.rescan(use_database=False) # Verify rescan completed app.serie_scanner.scan.assert_called_once() @@ -297,7 +297,7 @@ class TestSeriesAppReScan: async def test_rescan_cancellation( self, mock_serie_list, mock_scanner, mock_loaders ): - """Test rescan cancellation.""" + """Test rescan cancellation (file-based mode).""" test_dir = "/test/anime" app = SeriesApp(test_dir) @@ -313,9 +313,9 @@ class TestSeriesAppReScan: app.serie_scanner.scan = Mock(side_effect=mock_scan) - # Perform rescan - should handle cancellation + # Perform rescan - should handle cancellation (file-based mode) try: - await app.rescan() + await app.rescan(use_database=False) except Exception: pass # Cancellation is expected