Add data file to database sync functionality
- Add get_all_series_from_data_files() to SeriesApp - Sync series from data files to DB on startup - Add unit tests for new SeriesApp method - Add integration tests for sync functionality - Update documentation
This commit is contained in:
@@ -599,3 +599,56 @@ class SeriesApp:
|
||||
looks up series by their unique key, not by folder name.
|
||||
"""
|
||||
return self.list.get_by_key(key)
|
||||
|
||||
def get_all_series_from_data_files(self) -> List[Serie]:
|
||||
"""
|
||||
Get all series from data files in the anime directory.
|
||||
|
||||
Scans the directory_to_search for all 'data' files and loads
|
||||
the Serie metadata from each file. This method is synchronous
|
||||
and can be wrapped with asyncio.to_thread if needed for async
|
||||
contexts.
|
||||
|
||||
Returns:
|
||||
List of Serie objects found in data files. Returns an empty
|
||||
list if no data files are found or if the directory doesn't
|
||||
exist.
|
||||
|
||||
Example:
|
||||
series_app = SeriesApp("/path/to/anime")
|
||||
all_series = series_app.get_all_series_from_data_files()
|
||||
for serie in all_series:
|
||||
print(f"Found: {serie.name} (key={serie.key})")
|
||||
"""
|
||||
logger.info(
|
||||
"Scanning for data files in directory: %s",
|
||||
self.directory_to_search
|
||||
)
|
||||
|
||||
# Create a fresh SerieList instance for file-based loading
|
||||
# This ensures we get all series from data files without
|
||||
# interfering with the main instance's state
|
||||
try:
|
||||
temp_list = SerieList(
|
||||
self.directory_to_search,
|
||||
db_session=None, # Force file-based loading
|
||||
skip_load=False # Allow automatic loading
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
"Failed to scan directory for data files: %s",
|
||||
str(e),
|
||||
exc_info=True
|
||||
)
|
||||
return []
|
||||
|
||||
# Get all series from the temporary list
|
||||
all_series = temp_list.get_all()
|
||||
|
||||
logger.info(
|
||||
"Found %d series from data files in %s",
|
||||
len(all_series),
|
||||
self.directory_to_search
|
||||
)
|
||||
|
||||
return all_series
|
||||
|
||||
@@ -41,6 +41,78 @@ from src.server.services.websocket_service import get_websocket_service
|
||||
# module-level globals. This makes testing and multi-instance hosting safer.
|
||||
|
||||
|
||||
async def _sync_series_to_database(
|
||||
anime_directory: str,
|
||||
logger
|
||||
) -> int:
|
||||
"""
|
||||
Sync series from data files to the database.
|
||||
|
||||
Scans the anime directory for data files and adds any new series
|
||||
to the database. Existing series are skipped (no duplicates).
|
||||
|
||||
Args:
|
||||
anime_directory: Path to the anime directory with data files
|
||||
logger: Logger instance for logging operations
|
||||
|
||||
Returns:
|
||||
Number of new series added to the database
|
||||
"""
|
||||
try:
|
||||
import asyncio
|
||||
|
||||
from src.core.entities.SerieList import SerieList
|
||||
from src.core.SeriesApp import SeriesApp
|
||||
from src.server.database.connection import get_db_session
|
||||
|
||||
# Get all series from data files using SeriesApp
|
||||
series_app = SeriesApp(anime_directory)
|
||||
all_series = await asyncio.to_thread(
|
||||
series_app.get_all_series_from_data_files
|
||||
)
|
||||
|
||||
if not all_series:
|
||||
logger.info("No series found in data files to sync")
|
||||
return 0
|
||||
|
||||
logger.info(
|
||||
"Found %d series in data files, syncing to database...",
|
||||
len(all_series)
|
||||
)
|
||||
|
||||
async with get_db_session() as db:
|
||||
serie_list = SerieList(
|
||||
anime_directory,
|
||||
db_session=db,
|
||||
skip_load=True
|
||||
)
|
||||
added_count = 0
|
||||
for serie in all_series:
|
||||
result = await serie_list.add_to_db(serie, db)
|
||||
if result:
|
||||
added_count += 1
|
||||
logger.debug(
|
||||
"Added series to database: %s (key=%s)",
|
||||
serie.name,
|
||||
serie.key
|
||||
)
|
||||
# Commit happens automatically via get_db_session context
|
||||
logger.info(
|
||||
"Synced %d new series to database (skipped %d existing)",
|
||||
added_count,
|
||||
len(all_series) - added_count
|
||||
)
|
||||
return added_count
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(
|
||||
"Failed to sync series to database: %s",
|
||||
e,
|
||||
exc_info=True
|
||||
)
|
||||
return 0
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
"""Manage application lifespan (startup and shutdown)."""
|
||||
@@ -104,6 +176,11 @@ async def lifespan(app: FastAPI):
|
||||
download_service = get_download_service()
|
||||
await download_service.initialize()
|
||||
logger.info("Download service initialized and queue restored")
|
||||
|
||||
# Sync series from data files to database
|
||||
await _sync_series_to_database(
|
||||
settings.anime_directory, logger
|
||||
)
|
||||
else:
|
||||
logger.info(
|
||||
"Download service initialization skipped - "
|
||||
|
||||
Reference in New Issue
Block a user