Jellyfin [ALL]: Fix search for generated season titles#585
Conversation
Reviewer's GuideAdjusts Jellyfin search behavior to better handle season-style titles by normalizing search terms, optionally re-querying without season suffixes, and then post-filtering results, while also bumping the extension version code. Sequence diagram for updated Jellyfin season title search behaviorsequenceDiagram
actor User
participant Jellyfin as Jellyfin.searchAnime
participant Client as client
User->>Jellyfin: searchAnime(query, page)
Jellyfin->>Jellyfin: checkPreferences()
Jellyfin->>Jellyfin: getSearchUrl(searchQuery)
Jellyfin->>Client: get(url)
Client-->>Jellyfin: parseAs_ItemListDto(json) as items
Jellyfin->>Jellyfin: seriesQuery = searchQuery.replace(SEASON_TITLE_SUFFIX_REGEX, "")
alt [seriesQuery != searchQuery && items.items.none { it.name.equals(searchQuery, ignoreCase = true) }]
Jellyfin->>Jellyfin: getSearchUrl(seriesQuery)
Jellyfin->>Client: get(url)
Client-->>Jellyfin: parseAs_ItemListDto(json) as items
Jellyfin->>Jellyfin: requestedSeasonTitle = searchQuery
else [no season requery]
Jellyfin->>Jellyfin: requestedSeasonTitle = null
end
Jellyfin->>Jellyfin: items.items.parallelFlatMap { ... } as animeList
alt [requestedSeasonTitle != null]
Jellyfin->>Jellyfin: matchingAnime = animeList.filter { it.title.equals(requestedSeasonTitle, ignoreCase = true) }
Jellyfin->>Jellyfin: hasNextPage = false
else
Jellyfin->>Jellyfin: matchingAnime = animeList
Jellyfin->>Jellyfin: hasNextPage = SERIES_FETCH_LIMIT * page < items.totalRecordCount
end
Jellyfin-->>User: AnimesPage(matchingAnime, hasNextPage)
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've left some high level feedback:
- The second
parseAscall for the series search (client.get(getSearchUrl(seriesQuery)).parseAs(json)) drops the explicitItemListDtotype parameter; consider restoring it to avoid relying on type inference and potential mismatches. - The
hasNextPagelogic now depends onrequestedSeasonTitlebeing null; double-check if this behavior (disabling pagination when a season title was requested) is intentional for all search scenarios, or if it should still respectitems.totalRecordCount.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The second `parseAs` call for the series search (`client.get(getSearchUrl(seriesQuery)).parseAs(json)`) drops the explicit `ItemListDto` type parameter; consider restoring it to avoid relying on type inference and potential mismatches.
- The `hasNextPage` logic now depends on `requestedSeasonTitle` being null; double-check if this behavior (disabling pagination when a season title was requested) is intentional for all search scenarios, or if it should still respect `items.totalRecordCount`.Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
There was a problem hiding this comment.
Code Review
This pull request improves the Jellyfin extension's search behavior when searching for specific seasons. If a search for a specific season fails, it strips the season suffix to search for the base series instead, and then filters the results to match the requested season. The review feedback points out that disabling pagination during season searches limits results to the first page (5 items), which may prevent finding the target series if it appears on later pages. Additionally, the reviewer suggests normalizing season titles (e.g., handling zero-padded season numbers like "Season 01" vs "Season 1") to make the matching more robust.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| val matchingAnime = requestedSeasonTitle?.let { title -> | ||
| animeList.filter { it.title.equals(title, ignoreCase = true) } | ||
| } ?: animeList | ||
| val hasNextPage = requestedSeasonTitle == null && SERIES_FETCH_LIMIT * page < items.totalRecordCount |
There was a problem hiding this comment.
Disabling pagination when requestedSeasonTitle != null limits the search results to only the first page of series (up to 5 items, since SERIES_FETCH_LIMIT is 5). If the target series is not within the first 5 results, the user will never be able to find the requested season.
Removing requestedSeasonTitle == null && allows pagination to work correctly for season searches, as the next pages of series will be fetched and filtered as expected.
| val hasNextPage = requestedSeasonTitle == null && SERIES_FETCH_LIMIT * page < items.totalRecordCount | |
| val hasNextPage = SERIES_FETCH_LIMIT * page < items.totalRecordCount |
| private val SEASON_TITLE_SUFFIX_REGEX = Regex( | ||
| """\s+(?:Season\s+\d+|Specials)\s*$""", | ||
| RegexOption.IGNORE_CASE, | ||
| ) |
There was a problem hiding this comment.
Add a regex to normalize season titles (e.g., stripping leading zeros from season numbers) to ensure robust matching during season searches.
private val SEASON_TITLE_SUFFIX_REGEX = Regex(
"""\s+(?:Season\s+\d+|Specials)\s*$""",
RegexOption.IGNORE_CASE,
)
private val SEASON_NORMALIZE_REGEX = Regex(
"""\bSeason\s+0*(\d+)\b""",
RegexOption.IGNORE_CASE,
)| val matchingAnime = requestedSeasonTitle?.let { title -> | ||
| animeList.filter { it.title.equals(title, ignoreCase = true) } | ||
| } ?: animeList |
There was a problem hiding this comment.
When searching for a specific season, the search query might contain a different zero-padding for the season number compared to how it is named in Jellyfin (e.g., Season 01 vs Season 1). To make the matching robust against such differences, we can normalize the season numbers by stripping leading zeros before comparing the titles.
| val matchingAnime = requestedSeasonTitle?.let { title -> | |
| animeList.filter { it.title.equals(title, ignoreCase = true) } | |
| } ?: animeList | |
| val matchingAnime = requestedSeasonTitle?.let { title -> | |
| val normalizedTitle = title.replace(SEASON_NORMALIZE_REGEX, "Season $1") | |
| animeList.filter { | |
| it.title.replace(SEASON_NORMALIZE_REGEX, "Season $1").equals(normalizedTitle, ignoreCase = true) | |
| } | |
| } ?: animeList |
|
Some context for this change: Jellyfin season entries are displayed as |
Checklist:
extVersionCodevalue inbuild.gradlefor individual extensionsoverrideVersionCodeorbaseVersionCodeas needed for all multisrc extensionsisNsfw = trueflag inbuild.gradlewhen appropriateidif a source's name or language were changedweb_hi_res_512.pngwhen adding a new extensionAdd a 👍 reaction to pull requests you find important.
Summary by Sourcery
Improve Jellyfin search handling to correctly find series when users search using generated season titles.
Bug Fixes:
Enhancements:
Build: