fix(folder_scan): await NFO repair before folder rename
folder_rename_service depends on clean NFO files but repair tasks were fire-and-forget. Now collect all repair tasks and await them with asyncio.gather before validate_and_rename_series_folders runs. Also update tests that mock asyncio.create_task to also mock asyncio.gather since perform_nfo_repair_scan now awaits tasks.
This commit is contained in:
@@ -129,6 +129,7 @@ async def perform_nfo_repair_scan(background_loader=None) -> None:
|
||||
queued = 0
|
||||
total = 0
|
||||
missing_nfo_count = 0
|
||||
repair_tasks: list[asyncio.Task] = []
|
||||
for series_dir in sorted(anime_dir.iterdir()):
|
||||
if not series_dir.is_dir():
|
||||
continue
|
||||
@@ -137,19 +138,31 @@ async def perform_nfo_repair_scan(background_loader=None) -> None:
|
||||
if not nfo_path.exists():
|
||||
# Create minimal NFO for series without one
|
||||
missing_nfo_count += 1
|
||||
asyncio.create_task(
|
||||
_create_missing_nfo(series_dir, series_name),
|
||||
name=f"nfo_create:{series_name}",
|
||||
repair_tasks.append(
|
||||
asyncio.create_task(
|
||||
_create_missing_nfo(series_dir, series_name),
|
||||
name=f"nfo_create:{series_name}",
|
||||
)
|
||||
)
|
||||
continue
|
||||
total += 1
|
||||
if nfo_needs_repair(nfo_path):
|
||||
queued += 1
|
||||
asyncio.create_task(
|
||||
_repair_one_series(series_dir, series_name),
|
||||
name=f"nfo_repair:{series_name}",
|
||||
repair_tasks.append(
|
||||
asyncio.create_task(
|
||||
_repair_one_series(series_dir, series_name),
|
||||
name=f"nfo_repair:{series_name}",
|
||||
)
|
||||
)
|
||||
|
||||
if repair_tasks:
|
||||
logger.info(
|
||||
"NFO repair scan: waiting for %d repair/create tasks to complete",
|
||||
len(repair_tasks),
|
||||
)
|
||||
await asyncio.gather(*repair_tasks, return_exceptions=True)
|
||||
logger.info("NFO repair scan tasks completed")
|
||||
|
||||
logger.info(
|
||||
"NFO repair scan complete: %d of %d series queued for repair, %d missing NFOs queued for creation",
|
||||
queued,
|
||||
@@ -182,10 +195,10 @@ class FolderScanService:
|
||||
if not self._prerequisites_met():
|
||||
return
|
||||
|
||||
# 1.3 — Repair incomplete NFO files in the background.
|
||||
# 1.3 — Repair incomplete NFO files (synchronous, waits for completion).
|
||||
logger.info("Starting NFO repair scan as part of folder scan")
|
||||
await perform_nfo_repair_scan(background_loader=None)
|
||||
logger.info("NFO repair scan queued; repairs will continue in background")
|
||||
logger.info("NFO repair scan complete")
|
||||
|
||||
# 1.4 — Validate and rename series folders after NFO repair.
|
||||
logger.info("Starting folder rename validation")
|
||||
|
||||
@@ -816,11 +816,14 @@ class TestPerformNfoRepairScan:
|
||||
return_value=mock_repair_service,
|
||||
), patch(
|
||||
"asyncio.create_task"
|
||||
) as mock_create_task:
|
||||
) as mock_create_task, patch(
|
||||
"asyncio.gather", new_callable=AsyncMock
|
||||
) as mock_gather:
|
||||
mock_factory_cls.return_value.create.return_value = MagicMock()
|
||||
await perform_nfo_repair_scan(background_loader=AsyncMock())
|
||||
|
||||
mock_create_task.assert_called_once()
|
||||
mock_gather.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_skips_complete_series(self, tmp_path):
|
||||
@@ -876,8 +879,11 @@ class TestPerformNfoRepairScan:
|
||||
return_value=mock_repair_service,
|
||||
), patch(
|
||||
"asyncio.create_task"
|
||||
) as mock_create_task:
|
||||
) as mock_create_task, patch(
|
||||
"asyncio.gather", new_callable=AsyncMock
|
||||
) as mock_gather:
|
||||
mock_factory_cls.return_value.create.return_value = MagicMock()
|
||||
await perform_nfo_repair_scan(background_loader=None)
|
||||
|
||||
mock_create_task.assert_called_once()
|
||||
mock_gather.assert_called_once()
|
||||
|
||||
Reference in New Issue
Block a user