feat(setup): add SetupService for anime folder initialization

Extract SetupService class from initialization_service to handle:
- Scan data/ folder subdirectories
- Extract title and year from folder names (YYYY pattern)
- Create AnimeSeries records in database
- Resolve provider keys via search (single exact match)

Updates _scan_folders_to_database() to delegate to SetupService.run().
Adds comprehensive unit tests for SetupService.
This commit is contained in:
2026-06-05 19:54:45 +02:00
parent 2c47713339
commit 5c2be3f7c4
4 changed files with 656 additions and 239 deletions

View File

@@ -10,6 +10,7 @@ import structlog
from src.config.settings import settings
from src.server.database.service import AnimeSeriesService
from src.server.services.anime_service import sync_legacy_series_to_db
from src.server.services.setup_service import SetupService
logger = structlog.get_logger(__name__)
@@ -259,7 +260,8 @@ async def _load_series_into_memory(progress_service=None) -> None:
async def _scan_folders_to_database(progress_service=None) -> int:
"""Scan anime folders and create AnimeSeries DB records.
This function runs during initial setup only. It:
This function runs during initial setup only. It delegates to
SetupService.run() which handles:
1. Iterates subdirectories of anime_directory
2. Extracts title/year from folder names (year via (YYYY) pattern)
3. Uses provider search to resolve key field when single match found
@@ -271,9 +273,6 @@ async def _scan_folders_to_database(progress_service=None) -> int:
Returns:
int: Number of new series created
"""
from src.server.database.connection import get_db_session
from src.server.utils.dependencies import get_series_app
logger.info("Scanning anime folders for new series...")
if not settings.anime_directory or not os.path.isdir(settings.anime_directory):
@@ -282,80 +281,12 @@ async def _scan_folders_to_database(progress_service=None) -> int:
)
return 0
created_count = 0
skipped_existing = 0
try:
series_app = get_series_app()
async with get_db_session() as db:
for folder in settings.anime_directory.iterdir():
if not folder.is_dir():
continue
folder_name = folder.name
# Skip if series already exists in DB
existing = await AnimeSeriesService.get_by_folder(db, folder_name)
if existing:
skipped_existing += 1
continue
# Extract year from folder name using (YYYY) pattern
year = None
match = re.search(r'\((\d{4})\)', folder_name)
if match:
year = int(match.group(1))
# Extract title by removing year suffix
title = re.sub(r'\s*\(\d{4}\)\s*$', '', folder_name).strip()
# Try to resolve key via provider search
resolved_key = ""
if title:
try:
results = await series_app.search(title)
if len(results) == 1:
result_name = results[0].get('name', '').lower()
if result_name == title.lower():
resolved_key = results[0].get('key', '')
except Exception as exc:
logger.warning(
"Provider search failed for folder",
folder=folder_name,
error=str(exc)
)
# Create AnimeSeries record
await AnimeSeriesService.create(
db=db,
key=resolved_key,
name=title,
site=ANIMEWORLD_URL,
folder=folder_name,
year=year,
)
created_count += 1
logger.debug(
"Created series from folder",
folder=folder_name,
title=title,
year=year,
key=resolved_key or "(unresolved)"
)
except Exception as exc:
logger.error(
"Folder scan failed",
error=str(exc),
exc_info=True
)
return created_count
# Use SetupService to handle the scanning and creation
created_count = await SetupService.run()
logger.info(
"Folder scan complete",
created=created_count,
skipped_existing=skipped_existing
created=created_count
)
return created_count