Complete Task 8: Database Support for NFO Status
- Added 5 NFO tracking fields to AnimeSeries model - Fields: has_nfo, nfo_created_at, nfo_updated_at, tmdb_id, tvdb_id - Added 3 service methods to AnimeService for NFO operations - Methods: update_nfo_status, get_series_without_nfo, get_nfo_statistics - SQLAlchemy auto-migration (no manual migration needed) - Backward compatible with existing data - 15 new tests added (19/19 passing) - Tests: database models, service methods, integration queries
This commit is contained in:
@@ -863,6 +863,215 @@ class AnimeService:
|
||||
logger.exception("download failed")
|
||||
raise AnimeServiceError("Download failed") from exc
|
||||
|
||||
async def update_nfo_status(
|
||||
self,
|
||||
key: str,
|
||||
has_nfo: bool,
|
||||
tmdb_id: Optional[int] = None,
|
||||
tvdb_id: Optional[int] = None,
|
||||
db=None
|
||||
) -> None:
|
||||
"""Update NFO status for a series in the database.
|
||||
|
||||
Args:
|
||||
key: Serie unique identifier
|
||||
has_nfo: Whether tvshow.nfo exists
|
||||
tmdb_id: Optional TMDB ID
|
||||
tvdb_id: Optional TVDB ID
|
||||
db: Optional database session (will create if not provided)
|
||||
|
||||
Raises:
|
||||
AnimeServiceError: If update fails
|
||||
"""
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from src.server.database.connection import get_db_session
|
||||
from src.server.database.models import AnimeSeries
|
||||
|
||||
try:
|
||||
# Get or create database session
|
||||
should_close = False
|
||||
if db is None:
|
||||
db = get_db_session()
|
||||
should_close = True
|
||||
|
||||
try:
|
||||
# Find series by key
|
||||
series = db.query(AnimeSeries).filter(
|
||||
AnimeSeries.key == key
|
||||
).first()
|
||||
|
||||
if not series:
|
||||
logger.warning(
|
||||
"Series not found in database for NFO update",
|
||||
key=key
|
||||
)
|
||||
return
|
||||
|
||||
# Update NFO fields
|
||||
now = datetime.now(timezone.utc)
|
||||
series.has_nfo = has_nfo
|
||||
|
||||
if has_nfo:
|
||||
if series.nfo_created_at is None:
|
||||
series.nfo_created_at = now
|
||||
series.nfo_updated_at = now
|
||||
|
||||
if tmdb_id is not None:
|
||||
series.tmdb_id = tmdb_id
|
||||
|
||||
if tvdb_id is not None:
|
||||
series.tvdb_id = tvdb_id
|
||||
|
||||
db.commit()
|
||||
logger.info(
|
||||
"Updated NFO status in database",
|
||||
key=key,
|
||||
has_nfo=has_nfo,
|
||||
tmdb_id=tmdb_id,
|
||||
tvdb_id=tvdb_id
|
||||
)
|
||||
|
||||
finally:
|
||||
if should_close:
|
||||
db.close()
|
||||
|
||||
except Exception as exc:
|
||||
logger.exception(
|
||||
"Failed to update NFO status",
|
||||
key=key,
|
||||
has_nfo=has_nfo
|
||||
)
|
||||
raise AnimeServiceError("NFO status update failed") from exc
|
||||
|
||||
async def get_series_without_nfo(self, db=None) -> list[dict]:
|
||||
"""Get list of series that don't have NFO files.
|
||||
|
||||
Args:
|
||||
db: Optional database session
|
||||
|
||||
Returns:
|
||||
List of series dictionaries with keys:
|
||||
- key: Series unique identifier
|
||||
- name: Series name
|
||||
- folder: Series folder name
|
||||
- has_nfo: Always False
|
||||
- tmdb_id: TMDB ID if available
|
||||
- tvdb_id: TVDB ID if available
|
||||
|
||||
Raises:
|
||||
AnimeServiceError: If query fails
|
||||
"""
|
||||
from src.server.database.connection import get_db_session
|
||||
from src.server.database.models import AnimeSeries
|
||||
|
||||
try:
|
||||
# Get or create database session
|
||||
should_close = False
|
||||
if db is None:
|
||||
db = get_db_session()
|
||||
should_close = True
|
||||
|
||||
try:
|
||||
# Query series without NFO
|
||||
series_list = db.query(AnimeSeries).filter(
|
||||
AnimeSeries.has_nfo == False # noqa: E712
|
||||
).all()
|
||||
|
||||
result = []
|
||||
for series in series_list:
|
||||
result.append({
|
||||
"key": series.key,
|
||||
"name": series.name,
|
||||
"folder": series.folder,
|
||||
"has_nfo": False,
|
||||
"tmdb_id": series.tmdb_id,
|
||||
"tvdb_id": series.tvdb_id,
|
||||
"nfo_created_at": None,
|
||||
"nfo_updated_at": None
|
||||
})
|
||||
|
||||
logger.info(
|
||||
"Retrieved series without NFO",
|
||||
count=len(result)
|
||||
)
|
||||
return result
|
||||
|
||||
finally:
|
||||
if should_close:
|
||||
db.close()
|
||||
|
||||
except Exception as exc:
|
||||
logger.exception("Failed to query series without NFO")
|
||||
raise AnimeServiceError(
|
||||
"Query for series without NFO failed"
|
||||
) from exc
|
||||
|
||||
async def get_nfo_statistics(self, db=None) -> dict:
|
||||
"""Get NFO statistics for all series.
|
||||
|
||||
Args:
|
||||
db: Optional database session
|
||||
|
||||
Returns:
|
||||
Dictionary with statistics:
|
||||
- total: Total series count
|
||||
- with_nfo: Series with NFO files
|
||||
- without_nfo: Series without NFO files
|
||||
- with_tmdb_id: Series with TMDB ID
|
||||
- with_tvdb_id: Series with TVDB ID
|
||||
|
||||
Raises:
|
||||
AnimeServiceError: If query fails
|
||||
"""
|
||||
from src.server.database.connection import get_db_session
|
||||
from src.server.database.models import AnimeSeries
|
||||
|
||||
try:
|
||||
# Get or create database session
|
||||
should_close = False
|
||||
if db is None:
|
||||
db = get_db_session()
|
||||
should_close = True
|
||||
|
||||
try:
|
||||
# Count total series
|
||||
total = db.query(AnimeSeries).count()
|
||||
|
||||
# Count series with NFO
|
||||
with_nfo = db.query(AnimeSeries).filter(
|
||||
AnimeSeries.has_nfo == True # noqa: E712
|
||||
).count()
|
||||
|
||||
# Count series with TMDB ID
|
||||
with_tmdb = db.query(AnimeSeries).filter(
|
||||
AnimeSeries.tmdb_id.isnot(None)
|
||||
).count()
|
||||
|
||||
# Count series with TVDB ID
|
||||
with_tvdb = db.query(AnimeSeries).filter(
|
||||
AnimeSeries.tvdb_id.isnot(None)
|
||||
).count()
|
||||
|
||||
stats = {
|
||||
"total": total,
|
||||
"with_nfo": with_nfo,
|
||||
"without_nfo": total - with_nfo,
|
||||
"with_tmdb_id": with_tmdb,
|
||||
"with_tvdb_id": with_tvdb
|
||||
}
|
||||
|
||||
logger.info("Retrieved NFO statistics", **stats)
|
||||
return stats
|
||||
|
||||
finally:
|
||||
if should_close:
|
||||
db.close()
|
||||
|
||||
except Exception as exc:
|
||||
logger.exception("Failed to get NFO statistics")
|
||||
raise AnimeServiceError("NFO statistics query failed") from exc
|
||||
|
||||
|
||||
def get_anime_service(series_app: SeriesApp) -> AnimeService:
|
||||
"""Factory used for creating AnimeService with a SeriesApp instance."""
|
||||
|
||||
Reference in New Issue
Block a user