fix: Correct series filter logic for no_episodes

Critical bug fix: The filter was returning the wrong series because of
a misunderstanding of the episode table semantics.

ISSUE:
- Episodes table contains MISSING episodes (from episodeDict)
- is_downloaded=False means episode file not found in folder
- Original query logic was backwards - returned series with NO missing
  episodes instead of series WITH missing episodes

SOLUTION:
- Simplified query to directly check for episodes with is_downloaded=False
- Changed from complex join with count aggregation to simple subquery
- Now correctly returns series that have at least one undownloaded episode

CHANGES:
- src/server/database/service.py: Rewrote get_series_with_no_episodes()
  method with corrected logic and clearer documentation
- tests/unit/test_series_filter.py: Updated test expectations to match
  corrected behavior with detailed comments explaining episode semantics
- docs/API.md: Enhanced documentation explaining filter behavior and
  episode table meaning

TESTS:
All 5 unit tests pass with corrected logic
This commit is contained in:
2026-01-23 19:11:41 +01:00
parent 5af72c33b8
commit 04f26d5cfc
4 changed files with 165 additions and 105 deletions

View File

@@ -26,7 +26,7 @@ import logging
from datetime import datetime, timedelta, timezone
from typing import List, Optional
from sqlalchemy import delete, select, update
from sqlalchemy import Integer, delete, select, update
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import Session, selectinload
@@ -258,11 +258,15 @@ class AnimeSeriesService:
limit: Optional[int] = None,
offset: int = 0,
) -> List[AnimeSeries]:
"""Get anime series that have no downloaded episodes in folder.
"""Get anime series that have no episodes found in folder.
Returns series where either:
- No episodes exist in the database, OR
- All episodes have is_downloaded=False
Since episodes in the database represent MISSING episodes
(from episodeDict), this returns series that have episodes
in the DB with is_downloaded=False, meaning they have missing
episodes and no files were found in the folder for those episodes.
Returns series where:
- At least one episode exists in database with is_downloaded=False
Args:
db: Database session
@@ -270,31 +274,20 @@ class AnimeSeriesService:
offset: Offset for pagination
Returns:
List of AnimeSeries instances with no downloaded episodes
List of AnimeSeries with missing episodes (not in folder)
"""
from sqlalchemy import func, or_
# Subquery to count downloaded episodes per series
downloaded_count = (
select(
Episode.series_id,
func.count(Episode.id).label('downloaded_count')
)
.where(Episode.is_downloaded.is_(True))
.group_by(Episode.series_id)
# Subquery to find series IDs with at least one undownloaded episode
undownloaded_series_ids = (
select(Episode.series_id)
.where(Episode.is_downloaded == False)
.distinct()
.subquery()
)
# Select series that either have no episodes or no downloaded episodes
# Select series that have undownloaded episodes
query = (
select(AnimeSeries)
.outerjoin(downloaded_count, AnimeSeries.id == downloaded_count.c.series_id)
.where(
or_(
downloaded_count.c.downloaded_count == None,
downloaded_count.c.downloaded_count == 0
)
)
.where(AnimeSeries.id.in_(select(undownloaded_series_ids.c.series_id)))
.order_by(AnimeSeries.name)
.offset(offset)
)