refactor(scheduler): separate scheduler logic from scan/rescan logic

- Extract rescan logic into new RescanService (src/server/services/rescan_service.py)
- SchedulerService now only handles APScheduler cron scheduling
- Move scheduler sub-services (folder_rename, folder_scan, key_resolution) to scheduler/ folder
- Keep RescanOrchestrator as backward-compatible alias
- Update all imports across api/, server/, and test files
This commit is contained in:
2026-06-03 20:58:30 +02:00
parent 9d64241230
commit 9c3f03d610
25 changed files with 1080 additions and 578 deletions

View File

@@ -15,7 +15,7 @@ from unittest.mock import AsyncMock, MagicMock, Mock, patch
import pytest
from src.server.services.folder_scan_service import (
from src.server.services.scheduler.folder_scan_service import (
_POSTER_DOWNLOAD_SEMAPHORE,
_TMDB_SEMAPHORE,
FolderScanService,
@@ -97,7 +97,7 @@ class TestRunFolderScanPrerequisites:
with patch.object(
folder_scan_service, "_prerequisites_met", return_value=False
), patch(
"src.server.services.folder_scan_service.perform_nfo_repair_scan"
"src.server.services.scheduler.folder_scan_service.perform_nfo_repair_scan"
) as mock_repair:
await folder_scan_service.run_folder_scan()
mock_repair.assert_not_called()
@@ -108,10 +108,10 @@ class TestRunFolderScanPrerequisites:
with patch.object(
folder_scan_service, "_prerequisites_met", return_value=True
), patch(
"src.server.services.folder_scan_service.perform_nfo_repair_scan",
"src.server.services.scheduler.folder_scan_service.perform_nfo_repair_scan",
new_callable=AsyncMock,
), patch(
"src.server.services.folder_rename_service.validate_and_rename_series_folders",
"src.server.services.scheduler.folder_rename_service.validate_and_rename_series_folders",
new_callable=AsyncMock,
return_value={"scanned": 0, "renamed": 0, "skipped": 0, "errors": 0},
), patch.object(
@@ -148,10 +148,10 @@ class TestNfoRepairIntegration:
with patch.object(
folder_scan_service, "_prerequisites_met", return_value=True
), patch(
"src.server.services.folder_scan_service.perform_nfo_repair_scan",
"src.server.services.scheduler.folder_scan_service.perform_nfo_repair_scan",
new_callable=AsyncMock,
) as mock_repair, patch(
"src.server.services.folder_rename_service.validate_and_rename_series_folders",
"src.server.services.scheduler.folder_rename_service.validate_and_rename_series_folders",
new_callable=AsyncMock,
return_value={"scanned": 0, "renamed": 0, "skipped": 0, "errors": 0},
), patch.object(
@@ -172,11 +172,11 @@ class TestNfoRepairIntegration:
with patch.object(
folder_scan_service, "_prerequisites_met", return_value=True
), patch(
"src.server.services.folder_scan_service.perform_nfo_repair_scan",
"src.server.services.scheduler.folder_scan_service.perform_nfo_repair_scan",
new_callable=AsyncMock,
side_effect=RuntimeError("repair failed"),
) as mock_repair, patch(
"src.server.services.folder_rename_service.validate_and_rename_series_folders",
"src.server.services.scheduler.folder_rename_service.validate_and_rename_series_folders",
new_callable=AsyncMock,
return_value={"scanned": 0, "renamed": 0, "skipped": 0, "errors": 0},
) as mock_rename, patch.object(
@@ -204,10 +204,10 @@ class TestFolderRenameIntegration:
with patch.object(
folder_scan_service, "_prerequisites_met", return_value=True
), patch(
"src.server.services.folder_scan_service.perform_nfo_repair_scan",
"src.server.services.scheduler.folder_scan_service.perform_nfo_repair_scan",
new_callable=AsyncMock,
), patch(
"src.server.services.folder_rename_service.validate_and_rename_series_folders",
"src.server.services.scheduler.folder_rename_service.validate_and_rename_series_folders",
new_callable=AsyncMock,
return_value={"scanned": 5, "renamed": 2, "skipped": 2, "errors": 1},
) as mock_rename, patch.object(
@@ -228,10 +228,10 @@ class TestFolderRenameIntegration:
with patch.object(
folder_scan_service, "_prerequisites_met", return_value=True
), patch(
"src.server.services.folder_scan_service.perform_nfo_repair_scan",
"src.server.services.scheduler.folder_scan_service.perform_nfo_repair_scan",
new_callable=AsyncMock,
), patch(
"src.server.services.folder_rename_service.validate_and_rename_series_folders",
"src.server.services.scheduler.folder_rename_service.validate_and_rename_series_folders",
new_callable=AsyncMock,
side_effect=RuntimeError("rename failed"),
), patch.object(
@@ -344,7 +344,7 @@ class TestPosterCheck:
mock_settings.nfo_download_poster = True
with patch(
"src.server.services.folder_scan_service.ImageDownloader",
"src.server.services.scheduler.folder_scan_service.ImageDownloader",
return_value=mock_downloader,
):
stats = await folder_scan_service.check_and_download_missing_posters()
@@ -423,7 +423,7 @@ class TestPosterCheck:
mock_settings.nfo_download_poster = True
with patch(
"src.server.services.folder_scan_service.ImageDownloader",
"src.server.services.scheduler.folder_scan_service.ImageDownloader",
return_value=mock_downloader,
):
stats = await folder_scan_service.check_and_download_missing_posters()
@@ -456,7 +456,7 @@ class TestPosterCheck:
mock_settings.nfo_download_poster = True
with patch(
"src.server.services.folder_scan_service.ImageDownloader",
"src.server.services.scheduler.folder_scan_service.ImageDownloader",
return_value=mock_downloader,
):
stats = await folder_scan_service.check_and_download_missing_posters()
@@ -491,7 +491,7 @@ class TestPosterCheck:
mock_settings.nfo_download_poster = True
with patch(
"src.server.services.folder_scan_service.ImageDownloader",
"src.server.services.scheduler.folder_scan_service.ImageDownloader",
return_value=mock_downloader,
):
stats = await folder_scan_service.check_and_download_missing_posters()
@@ -569,10 +569,10 @@ class TestRunFolderScanFull:
with patch.object(
folder_scan_service, "_prerequisites_met", return_value=True
), patch(
"src.server.services.folder_scan_service.perform_nfo_repair_scan",
"src.server.services.scheduler.folder_scan_service.perform_nfo_repair_scan",
new_callable=AsyncMock,
) as mock_repair, patch(
"src.server.services.folder_rename_service.validate_and_rename_series_folders",
"src.server.services.scheduler.folder_rename_service.validate_and_rename_series_folders",
new_callable=AsyncMock,
return_value={"scanned": 3, "renamed": 1, "skipped": 1, "errors": 1},
) as mock_rename, patch.object(
@@ -593,10 +593,10 @@ class TestRunFolderScanFull:
with patch.object(
folder_scan_service, "_prerequisites_met", return_value=True
), patch(
"src.server.services.folder_scan_service.perform_nfo_repair_scan",
"src.server.services.scheduler.folder_scan_service.perform_nfo_repair_scan",
new_callable=AsyncMock,
), patch(
"src.server.services.folder_rename_service.validate_and_rename_series_folders",
"src.server.services.scheduler.folder_rename_service.validate_and_rename_series_folders",
new_callable=AsyncMock,
return_value={"scanned": 0, "renamed": 0, "skipped": 0, "errors": 0},
), patch.object(