feat: rescan now saves to database instead of data files
- Update SeriesApp.rescan() to use database storage by default (use_database=True) - Use SerieScanner.scan_async() for database mode, which saves directly to DB - Fall back to legacy file-based scan() when use_database=False (for CLI compatibility) - Reinitialize SerieList from database after scan when in database mode - Update unit tests to use use_database=False for mocked tests - Add parameter to control storage mode for backward compatibility
This commit is contained in:
parent
3cb644add4
commit
1652f2f6af
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user