From 87a90a88bc0f642345fe85b76785458dafe19197 Mon Sep 17 00:00:00 2001 From: Garret Patti <42485635+garretpatti@users.noreply.github.com> Date: Sun, 5 Apr 2026 22:18:08 -0400 Subject: [PATCH] handle shows without season folders --- src/components/tv/TvView.tsx | 10 +++++++++- src/lib/tv.ts | 23 ++++++++++++++++++++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/components/tv/TvView.tsx b/src/components/tv/TvView.tsx index 78dad0c..5ffc521 100644 --- a/src/components/tv/TvView.tsx +++ b/src/components/tv/TvView.tsx @@ -73,7 +73,15 @@ export default function TvView({ libraryId }: Props) { setError(null) fetch(`/api/tv?libraryId=${encodeURIComponent(libraryId)}&seriesId=${encodeURIComponent(s.id)}`) .then((r) => r.json()) - .then((data) => { setSeasons(data); setLoading(false) }) + .then((data: TvSeason[]) => { + setSeasons(data) + setLoading(false) + // Flat series: a single synthetic season (id='.') means episodes live + // directly in the series folder — skip the seasons screen automatically. + if (data.length === 1 && data[0].id === '.') { + openSeason(data[0]) + } + }) .catch(() => { setError('Failed to load seasons'); setLoading(false) }) } diff --git a/src/lib/tv.ts b/src/lib/tv.ts index a4ef949..41bd620 100644 --- a/src/lib/tv.ts +++ b/src/lib/tv.ts @@ -83,10 +83,14 @@ export function scanTvLibrary(libraryRoot: string, libraryId: string): TvSeries[ const backdropFile = findFile(seriesPath, /^(backdrop|fanart|background)$/i) const seasonDirs = readDirs(seriesPath) - const seasonCount = seasonDirs.filter((sd) => { + const seasonDirCount = seasonDirs.filter((sd) => { const sdPath = path.join(seriesPath, sd) return countVideosInDir(sdPath) > 0 }).length + // If no season subdirectories contain videos, fall back to counting + // video files directly in the series root (flat/seasonless structure). + const seasonCount = + seasonDirCount > 0 ? seasonDirCount : countVideosInDir(seriesPath) > 0 ? 1 : 0 const id = encodeURIComponent(dirName) @@ -150,6 +154,23 @@ export function scanTvSeasons( }) } + // Flat series fallback: no season subdirectories have videos, but the + // series root itself does. Return a single synthetic season with id '.' + // so that scanTvEpisodes can scan the series directory directly. + if (seasons.length === 0 && countVideosInDir(seriesPath) > 0) { + const posterFile = findFile(seriesPath, /^(poster|folder)$/i) + seasons.push({ + id: '.', + seriesId, + title: 'Episodes', + seasonNumber: null, + posterUrl: posterFile + ? thumbnailApiUrl(libraryId, path.join(seriesDirName, posterFile)) + : null, + episodeCount: countVideosInDir(seriesPath), + }) + } + return seasons.sort((a, b) => { if (a.seasonNumber !== null && b.seasonNumber !== null) { return a.seasonNumber - b.seasonNumber