fix TV show metadata refresh
This commit is contained in:
@@ -120,7 +120,46 @@ export async function POST(request: NextRequest) {
|
|||||||
status: nfo.status ?? null,
|
status: nfo.status ?? null,
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
return NextResponse.json({ updated: true, title: nfo.title, year: nfo.year })
|
|
||||||
|
// Optionally also refresh every episode NFO in this series
|
||||||
|
let episodesUpdated = 0
|
||||||
|
const includeEpisodes = searchParams.get('includeEpisodes') === 'true'
|
||||||
|
if (includeEpisodes) {
|
||||||
|
type EpRow = { item_key: string; file_path: string | null; metadata: string | null }
|
||||||
|
const episodeRows = db
|
||||||
|
.prepare(`SELECT item_key, file_path, metadata FROM media_items WHERE item_type = 'tv_episode' AND item_key LIKE ?`)
|
||||||
|
.all(`${libraryId}:tv_episode:${encodedDirName}:%`) as EpRow[]
|
||||||
|
|
||||||
|
const updateEp = db.prepare(`
|
||||||
|
UPDATE media_items SET title = @title, plot = @plot, metadata = @metadata WHERE item_key = @item_key
|
||||||
|
`)
|
||||||
|
|
||||||
|
db.transaction(() => {
|
||||||
|
for (const ep of episodeRows) {
|
||||||
|
if (!ep.file_path) continue
|
||||||
|
const epDir = path.join(libraryRoot, path.dirname(ep.file_path))
|
||||||
|
const baseName = path.basename(ep.file_path, path.extname(ep.file_path))
|
||||||
|
const epNfo = parseEpisodeNfo(path.join(epDir, `${baseName}.nfo`))
|
||||||
|
if (!epNfo) continue
|
||||||
|
const epMeta = ep.metadata ? JSON.parse(ep.metadata) : {}
|
||||||
|
updateEp.run({
|
||||||
|
item_key: ep.item_key,
|
||||||
|
title: epNfo.title ?? null,
|
||||||
|
plot: epNfo.plot ?? null,
|
||||||
|
metadata: JSON.stringify({
|
||||||
|
...epMeta,
|
||||||
|
episodeNumber: epNfo.episode ?? epMeta.episodeNumber ?? null,
|
||||||
|
seasonNumber: epNfo.season ?? epMeta.seasonNumber ?? null,
|
||||||
|
aired: epNfo.aired ?? null,
|
||||||
|
rating: epNfo.rating ?? null,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
episodesUpdated++
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
}
|
||||||
|
|
||||||
|
return NextResponse.json({ updated: true, title: nfo.title, year: nfo.year, episodesUpdated })
|
||||||
}
|
}
|
||||||
|
|
||||||
if (itemType === 'tv_episode') {
|
if (itemType === 'tv_episode') {
|
||||||
|
|||||||
@@ -184,11 +184,18 @@ export default function TvView({ libraryId }: Props) {
|
|||||||
setRefreshingMeta(true)
|
setRefreshingMeta(true)
|
||||||
setWarnRefresh(false)
|
setWarnRefresh(false)
|
||||||
const itemKey = `${libraryId}:tv_series:${selectedSeries.id}`
|
const itemKey = `${libraryId}:tv_series:${selectedSeries.id}`
|
||||||
|
const currentId = selectedSeries.id
|
||||||
fetch(
|
fetch(
|
||||||
`/api/nfo-refresh?libraryId=${encodeURIComponent(libraryId)}&itemType=tv_series&itemKey=${encodeURIComponent(itemKey)}`,
|
`/api/nfo-refresh?libraryId=${encodeURIComponent(libraryId)}&itemType=tv_series&itemKey=${encodeURIComponent(itemKey)}&includeEpisodes=true`,
|
||||||
{ method: 'POST' }
|
{ method: 'POST' }
|
||||||
)
|
)
|
||||||
.then(() => fetchSeries())
|
.then(() => fetch(`/api/tv?libraryId=${encodeURIComponent(libraryId)}`))
|
||||||
|
.then((r) => r.json())
|
||||||
|
.then((data: TvSeries[]) => {
|
||||||
|
setSeries(data)
|
||||||
|
const updated = data.find((s) => s.id === currentId)
|
||||||
|
if (updated) setSelectedSeries(updated)
|
||||||
|
})
|
||||||
.finally(() => setRefreshingMeta(false))
|
.finally(() => setRefreshingMeta(false))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import path from 'path'
|
|||||||
import type { TvSeries, TvSeason, TvEpisode } from '@/types'
|
import type { TvSeries, TvSeason, TvEpisode } from '@/types'
|
||||||
import { getDb } from './db'
|
import { getDb } from './db'
|
||||||
import { HIDDEN_FILES, VIDEO_EXTENSIONS, fileApiUrl, thumbnailApiUrl, findFile } from './media-utils'
|
import { HIDDEN_FILES, VIDEO_EXTENSIONS, fileApiUrl, thumbnailApiUrl, findFile } from './media-utils'
|
||||||
|
import { parseTvShowNfo } from './nfo'
|
||||||
|
|
||||||
function isVideoFile(name: string): boolean {
|
function isVideoFile(name: string): boolean {
|
||||||
return VIDEO_EXTENSIONS.has(path.extname(name).toLowerCase())
|
return VIDEO_EXTENSIONS.has(path.extname(name).toLowerCase())
|
||||||
@@ -52,6 +53,7 @@ export function scanTvLibrary(libraryRoot: string, libraryId: string): TvSeries[
|
|||||||
|
|
||||||
const posterFile = findFile(seriesPath, /^(poster|folder)$/i)
|
const posterFile = findFile(seriesPath, /^(poster|folder)$/i)
|
||||||
const backdropFile = findFile(seriesPath, /^(backdrop|fanart|background)$/i)
|
const backdropFile = findFile(seriesPath, /^(backdrop|fanart|background)$/i)
|
||||||
|
const nfo = parseTvShowNfo(path.join(seriesPath, 'tvshow.nfo'))
|
||||||
|
|
||||||
const seasonDirs = readDirs(seriesPath)
|
const seasonDirs = readDirs(seriesPath)
|
||||||
const seasonDirCount = seasonDirs.filter((sd) => {
|
const seasonDirCount = seasonDirs.filter((sd) => {
|
||||||
@@ -67,11 +69,11 @@ export function scanTvLibrary(libraryRoot: string, libraryId: string): TvSeries[
|
|||||||
|
|
||||||
series.push({
|
series.push({
|
||||||
id,
|
id,
|
||||||
title: dirName,
|
title: nfo?.title ?? dirName,
|
||||||
year: null,
|
year: nfo?.year ?? null,
|
||||||
plot: null,
|
plot: nfo?.plot ?? null,
|
||||||
genres: [],
|
genres: nfo?.genres ?? [],
|
||||||
status: null,
|
status: nfo?.status ?? null,
|
||||||
posterUrl: posterFile
|
posterUrl: posterFile
|
||||||
? thumbnailApiUrl(libraryId, path.join(dirName, posterFile))
|
? thumbnailApiUrl(libraryId, path.join(dirName, posterFile))
|
||||||
: null,
|
: null,
|
||||||
|
|||||||
Reference in New Issue
Block a user