handle shows without season folders #10
@@ -73,7 +73,15 @@ export default function TvView({ libraryId }: Props) {
|
|||||||
setError(null)
|
setError(null)
|
||||||
fetch(`/api/tv?libraryId=${encodeURIComponent(libraryId)}&seriesId=${encodeURIComponent(s.id)}`)
|
fetch(`/api/tv?libraryId=${encodeURIComponent(libraryId)}&seriesId=${encodeURIComponent(s.id)}`)
|
||||||
.then((r) => r.json())
|
.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) })
|
.catch(() => { setError('Failed to load seasons'); setLoading(false) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -83,10 +83,14 @@ export function scanTvLibrary(libraryRoot: string, libraryId: string): TvSeries[
|
|||||||
const backdropFile = findFile(seriesPath, /^(backdrop|fanart|background)$/i)
|
const backdropFile = findFile(seriesPath, /^(backdrop|fanart|background)$/i)
|
||||||
|
|
||||||
const seasonDirs = readDirs(seriesPath)
|
const seasonDirs = readDirs(seriesPath)
|
||||||
const seasonCount = seasonDirs.filter((sd) => {
|
const seasonDirCount = seasonDirs.filter((sd) => {
|
||||||
const sdPath = path.join(seriesPath, sd)
|
const sdPath = path.join(seriesPath, sd)
|
||||||
return countVideosInDir(sdPath) > 0
|
return countVideosInDir(sdPath) > 0
|
||||||
}).length
|
}).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)
|
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) => {
|
return seasons.sort((a, b) => {
|
||||||
if (a.seasonNumber !== null && b.seasonNumber !== null) {
|
if (a.seasonNumber !== null && b.seasonNumber !== null) {
|
||||||
return a.seasonNumber - b.seasonNumber
|
return a.seasonNumber - b.seasonNumber
|
||||||
|
|||||||
Reference in New Issue
Block a user