Add filter for series with no downloaded episodes
- Added get_series_with_no_episodes() method to AnimeSeriesService - Updated list_anime endpoint to support filter='no_episodes' parameter - Added comprehensive unit tests for the new filtering functionality - All tests passing successfully
This commit is contained in:
@@ -237,7 +237,9 @@ async def list_anime(
|
||||
per_page: Items per page (must be positive, max 1000)
|
||||
sort_by: Optional sorting parameter. Allowed: title, id, name,
|
||||
missing_episodes
|
||||
filter: Optional filter parameter (validated for security)
|
||||
filter: Optional filter parameter. Allowed values:
|
||||
- "no_episodes": Show only series with no downloaded
|
||||
episodes in folder
|
||||
_auth: Ensures the caller is authenticated (value unused)
|
||||
series_app: Core SeriesApp instance provided via dependency.
|
||||
|
||||
@@ -308,6 +310,14 @@ async def list_anime(
|
||||
raise ValidationError(
|
||||
message="Invalid filter parameter"
|
||||
)
|
||||
|
||||
# Validate allowed filter values
|
||||
allowed_filters = ["no_episodes"]
|
||||
if filter not in allowed_filters:
|
||||
allowed = ", ".join(allowed_filters)
|
||||
raise ValidationError(
|
||||
message=f"Invalid filter value. Allowed: {allowed}"
|
||||
)
|
||||
|
||||
try:
|
||||
# Get all series from series app
|
||||
@@ -317,15 +327,24 @@ async def list_anime(
|
||||
series = series_app.list.GetList()
|
||||
summaries: List[AnimeSummary] = []
|
||||
|
||||
# Build a map of folder -> NFO data for efficient lookup
|
||||
# Build a map of folder -> NFO data and episode counts
|
||||
# for efficient lookup
|
||||
nfo_map = {}
|
||||
# Track series with no downloaded episodes
|
||||
series_with_no_episodes = set()
|
||||
|
||||
try:
|
||||
# Get all series from database to fetch NFO metadata
|
||||
# and episode counts
|
||||
from src.server.database.connection import get_sync_session
|
||||
from src.server.database.models import AnimeSeries as DBAnimeSeries
|
||||
from src.server.database.models import (
|
||||
AnimeSeries as DBAnimeSeries,
|
||||
Episode
|
||||
)
|
||||
|
||||
session = get_sync_session()
|
||||
try:
|
||||
# Get NFO data for all series
|
||||
db_series_list = session.query(DBAnimeSeries).all()
|
||||
for db_series in db_series_list:
|
||||
nfo_created = (
|
||||
@@ -342,12 +361,42 @@ async def list_anime(
|
||||
"nfo_updated_at": nfo_updated,
|
||||
"tmdb_id": db_series.tmdb_id,
|
||||
"tvdb_id": db_series.tvdb_id,
|
||||
"series_id": db_series.id,
|
||||
}
|
||||
|
||||
# If filter is "no_episodes", get series with
|
||||
# no downloaded episodes
|
||||
if filter == "no_episodes":
|
||||
# Query for series that have no downloaded episodes
|
||||
# This includes series with no episodes at all
|
||||
# or only undownloaded episodes
|
||||
series_ids_with_downloads = (
|
||||
session.query(Episode.series_id)
|
||||
.filter(Episode.is_downloaded.is_(True))
|
||||
.distinct()
|
||||
.all()
|
||||
)
|
||||
series_ids_with_downloads = {
|
||||
row[0] for row in series_ids_with_downloads
|
||||
}
|
||||
|
||||
# All series that are NOT in the downloaded set
|
||||
all_series_ids = {
|
||||
db_series.id for db_series in db_series_list
|
||||
}
|
||||
series_with_no_episodes_ids = (
|
||||
all_series_ids - series_ids_with_downloads
|
||||
)
|
||||
|
||||
# Map back to folder names for filtering
|
||||
for db_series in db_series_list:
|
||||
if db_series.id in series_with_no_episodes_ids:
|
||||
series_with_no_episodes.add(db_series.folder)
|
||||
finally:
|
||||
session.close()
|
||||
except Exception as e:
|
||||
logger.warning(f"Could not fetch NFO data from database: {e}")
|
||||
# Continue without NFO data if database query fails
|
||||
logger.warning(f"Could not fetch data from database: {e}")
|
||||
# Continue without filter data if database query fails
|
||||
|
||||
for serie in series:
|
||||
# Get all properties from the serie object
|
||||
@@ -357,6 +406,12 @@ async def list_anime(
|
||||
folder = getattr(serie, "folder", "")
|
||||
episode_dict = getattr(serie, "episodeDict", {}) or {}
|
||||
|
||||
# Apply filter if specified
|
||||
if filter == "no_episodes":
|
||||
# Skip series that are not in the no_episodes set
|
||||
if folder not in series_with_no_episodes:
|
||||
continue
|
||||
|
||||
# Convert episode dict keys to strings for JSON serialization
|
||||
missing_episodes = {str(k): v for k, v in episode_dict.items()}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user