fix(import): repli sur l'API Fankai quand une série est absente du scraper et retrait de Kodi des NFO

This commit is contained in:
ElPouki committed 2026-09-11 17:04:19 +02:00
1 parent ca971f4daf
commit 67794d92c8
6 files changed
+79 -36

No files matched your search

+19 -18
View File
@@ -5,8 +5,8 @@ import { requireAuth, requireAdmin } from '../auth.js'
import { logger } from '../logger.js'
import { readSettings } from '../settings.js'
import { getGitlabTitle } from '../gitlab-map.js'
import { readSerieData, loadEnrichedSeriesData } from '../lib/github-cache.js'
import { resolveEpNaming, computeExpectedName } from '../lib/serie-helpers.js'
import { loadEnrichedSeriesData } from '../lib/github-cache.js'
import { resolveEpNaming, computeExpectedName, resolveSerieData } from '../lib/serie-helpers.js'
import { GITLAB_API_NFO, GITLAB_RAW_NFO } from '../lib/nfo.js'
import { dispatchRemove } from '../torrent-clients/index.js'
import { readRequests, deleteRequest } from '../requests.js'
@@ -14,6 +14,8 @@ import { readOrganized, writeOrganized, updateOrganized, type Organized } from '
const router = Router()
const serieNotFound = (id: unknown) => `Série ${id} introuvable dans le scraper et sur l'API Fankai (voir les logs)`
// ── Import manuel ──────────────────────────────────────────────
router.post('/manual-import', requireAuth, async (req, res) => {
const { serie_id, items } = req.body
@@ -27,14 +29,8 @@ router.post('/manual-import', requireAuth, async (req, res) => {
// Les copies de fichiers sont asynchrones : les modifications sont rejouées sur une relecture fraîche à la fin
const ops: ((data: Organized) => void)[] = []
const apply = (op: (data: Organized) => void) => { op(organized); ops.push(op) }
let sd = await readSerieData(Number(serie_id))
if (!sd) {
// readSerieData ne couvre que le cache local — fallback sur le catalogue complet
// (utile pour les séries sans torrent officiel dont les données sont peut-être absentes du cache individuel)
const all = await loadEnrichedSeriesData()
sd = all.find((s: any) => s.id === Number(serie_id)) ?? null
}
if (!sd) { res.status(404).json({ error: 'Série introuvable dans le catalogue' }); return }
const sd = await resolveSerieData(Number(serie_id), 'Import manuel')
if (!sd) { res.status(404).json({ error: serieNotFound(serie_id) }); return }
const rawTitle = sd.title ?? sd.show_title ?? ''
const serieTitle = rawTitle.replace(/:/g, ' -').replace(/[<>"/\\|?*]/g, '').replace(/\s+/g, ' ').trim()
const episodeIndex = new Map<number, { ep: any; season: any }>()
@@ -177,8 +173,8 @@ router.post('/manual-import', requireAuth, async (req, res) => {
router.get('/organized/:serieId', requireAuth, async (req, res) => {
const serieId = Number(req.params.serieId)
try {
const sd = await readSerieData(serieId)
if (!sd) { res.status(404).json({ error: 'Série introuvable' }); return }
const sd = await resolveSerieData(serieId)
if (!sd) { res.status(404).json({ error: serieNotFound(serieId) }); return }
const organized = readOrganized()
const { nfoSupport } = readSettings()
const result: Record<string, any> = {}
@@ -212,8 +208,8 @@ router.post('/rename-episode', requireAuth, async (req, res) => {
const { serie_id, episode_id, torrent_hash } = req.body
if (!serie_id || !episode_id) { res.status(400).json({ error: 'serie_id et episode_id requis' }); return }
const { nfoSupport } = readSettings()
const sd = await readSerieData(Number(serie_id))
if (!sd) { res.status(404).json({ error: 'Série introuvable' }); return }
const sd = await resolveSerieData(Number(serie_id), 'Renommage')
if (!sd) { res.status(404).json({ error: serieNotFound(serie_id) }); return }
let organized: Organized
try { organized = readOrganized() }
catch (err) { res.status(500).json({ error: err instanceof Error ? err.message : 'organized.json illisible' }); return }
@@ -258,8 +254,8 @@ router.post('/rename-episode', requireAuth, async (req, res) => {
router.delete('/organized/:serieId', requireAuth, async (req, res) => {
const serieId = String(req.params.serieId)
const deleteFile = req.query.deleteFile === 'true'
const sd = await readSerieData(Number(serieId))
if (!sd) { res.status(404).json({ error: 'Série introuvable' }); return }
const sd = await resolveSerieData(Number(serieId), 'Désimport série')
if (!sd) { res.status(404).json({ error: serieNotFound(serieId) }); return }
let organized: Organized
try { organized = readOrganized() }
catch (err) { res.status(500).json({ error: err instanceof Error ? err.message : 'organized.json illisible' }); return }
@@ -320,8 +316,8 @@ router.delete('/organized/:serieId/seasons/:seasonId', requireAuth, async (req,
const serieId = String(req.params.serieId)
const seasonId = Number(req.params.seasonId)
const deleteFile = req.query.deleteFile === 'true'
const sd = await readSerieData(Number(serieId))
if (!sd) { res.status(404).json({ error: 'Série introuvable' }); return }
const sd = await resolveSerieData(Number(serieId), 'Désimport saison')
if (!sd) { res.status(404).json({ error: serieNotFound(serieId) }); return }
let organized: Organized
try { organized = readOrganized() }
catch (err) { res.status(500).json({ error: err instanceof Error ? err.message : 'organized.json illisible' }); return }
@@ -449,6 +445,11 @@ router.post('/rename-all', requireAdmin, async (req, res) => {
const onlyIds = Array.isArray(serie_ids) ? new Set(serie_ids.map(Number)) : serie_id ? new Set([Number(serie_id)]) : null
const { nfoSupport } = readSettings()
const seriesData = await loadEnrichedSeriesData()
if (onlyIds) {
const known = new Set(seriesData.map((sd: any) => sd.id))
const missing = await Promise.all([...onlyIds].filter(id => !known.has(id)).map(id => resolveSerieData(id, 'Rename masse')))
seriesData.push(...missing.filter(Boolean))
}
let organized: Organized
try { organized = readOrganized() }
catch (err) { res.status(500).json({ error: err instanceof Error ? err.message : 'organized.json illisible' }); return }