diff --git a/server/lib/serie-helpers.ts b/server/lib/serie-helpers.ts index 4ce7c51..349133c 100644 --- a/server/lib/serie-helpers.ts +++ b/server/lib/serie-helpers.ts @@ -1,3 +1,5 @@ +import path from 'path' + const FANKAI_API = 'https://metadata.fankai.fr' export async function fankaiGet(endpoint: string): Promise { @@ -67,6 +69,25 @@ export function resolveEpNaming(ep: any, hash?: string | null): { } } +/** + * Calcule le nom attendu d'un fichier épisode selon le mode (NFO ou formaté). + * Source unique de vérité partagée entre organized-summary, organized/:id, rename-episode et rename-all. + */ +export function computeExpectedName( + ep : any, + orgEntry: any, + hash : string | null, + nfoSupport: boolean, +): { currentName: string; expectedName: string; needsRename: boolean } { + const currentName = orgEntry.dest_path ? path.basename(orgEntry.dest_path) : orgEntry.dest_filename + const srcExt = path.extname(currentName) + const { formatted_name: rawFmt, nfo_filename: rawNfo } = resolveEpNaming(ep, hash) + const expectedName = nfoSupport + ? (rawNfo ? rawNfo.replace(/\.[^.]+$/, '') + srcExt : currentName) + : (rawFmt?.trim() ? rawFmt.replace(/[<>:"/\\|?*]/g, '').trim() + srcExt : currentName) + return { currentName, expectedName, needsRename: expectedName !== currentName } +} + /** * Dédoublonne une liste d'épisodes par episode_number. * Quand plusieurs variantes existent (ex. x264 + x265), on garde la première diff --git a/server/routes/import.ts b/server/routes/import.ts index f09184e..50d3326 100644 --- a/server/routes/import.ts +++ b/server/routes/import.ts @@ -7,7 +7,7 @@ import { DATA_DIR } from '../config.js' import { readSettings } from '../settings.js' import { getGitlabTitle } from '../gitlab-map.js' import { readSerieData, loadEnrichedSeriesData } from '../lib/github-cache.js' -import { resolveEpNaming } from '../lib/serie-helpers.js' +import { resolveEpNaming, computeExpectedName } from '../lib/serie-helpers.js' import { GITLAB_API_NFO, GITLAB_RAW_NFO } from '../lib/nfo.js' import { dispatchRemove } from '../torrent-clients/index.js' @@ -157,17 +157,8 @@ router.get('/organized/:serieId', requireAuth, async (req, res) => { } if (!entry) continue - // Calculer needs_rename côté serveur avec la même logique que le rename endpoint - const currentName = entry.dest_path ? path.basename(entry.dest_path) : entry.dest_filename - const srcExt = path.extname(currentName) - const { formatted_name: rawFmt, nfo_filename: rawNfo } = resolveEpNaming(ep, entryHash) - let expectedName: string - if (nfoSupport) { - expectedName = rawNfo ? rawNfo.replace(/\.[^.]+$/, '') + srcExt : currentName - } else { - expectedName = rawFmt?.trim() ? rawFmt.replace(/[<>:"/\\|?*]/g, '').trim() + srcExt : currentName - } - result[String(ep.id)] = { ...entry, hash: entryHash, needs_rename: expectedName !== currentName } + const { needsRename } = computeExpectedName(ep, entry, entryHash, nfoSupport) + result[String(ep.id)] = { ...entry, hash: entryHash, needs_rename: needsRename } } } res.json(result) @@ -202,16 +193,8 @@ router.post('/rename-episode', requireAuth, async (req, res) => { } } if (!orgEntry) { res.status(404).json({ error: 'Épisode non importé' }); return } - const currentName = orgEntry.dest_path ? path.basename(orgEntry.dest_path) : orgEntry.dest_filename - const srcExt = path.extname(currentName) - const { formatted_name: rawFmt, nfo_filename: rawNfo } = resolveEpNaming(ep, entryHash) - let newName: string - if (nfoSupport) { - newName = rawNfo ? rawNfo.replace(/\.[^.]+$/, '') + srcExt : currentName - } else { - newName = rawFmt?.trim() ? rawFmt.replace(/[<>:"/\\|?*]/g, '').trim() + srcExt : currentName - } - if (newName === currentName) { res.json({ ok: true, renamed: false, message: 'Nom déjà correct' }); return } + const { currentName, expectedName: newName, needsRename } = computeExpectedName(ep, orgEntry, entryHash, nfoSupport) + if (!needsRename) { res.json({ ok: true, renamed: false, message: 'Nom déjà correct' }); return } const oldPath = orgEntry.dest_path const newPath = path.join(path.dirname(oldPath), newName) try { @@ -355,17 +338,8 @@ router.get('/organized-summary', requireAuth, async (_req, res) => { if (eps[String(ep.id)]) { orgEntry = eps[String(ep.id)]; orgHash = hash; break } } if (!orgEntry) continue - const currentName = orgEntry.dest_path ? path.basename(orgEntry.dest_path) : orgEntry.dest_filename - const srcExt = path.extname(currentName) - const { formatted_name: rawFmt, nfo_filename: rawNfo } = resolveEpNaming(ep, orgHash) - let expectedName: string - if (nfoSupport) { - expectedName = rawNfo ? rawNfo.replace(/\.[^.]+$/, '') + srcExt : currentName - } else { - expectedName = rawFmt?.trim() ? rawFmt.replace(/[<>:"/\\|?*]/g, '').trim() + srcExt : currentName - } - const needsRename = expectedName !== currentName - const fileExists = orgEntry.dest_path ? fs.existsSync(orgEntry.dest_path) : false + const { currentName, expectedName, needsRename } = computeExpectedName(ep, orgEntry, orgHash, nfoSupport) + const fileExists = orgEntry.dest_path ? fs.existsSync(orgEntry.dest_path) : false episodes.push({ episode_id: ep.id, episode_number: ep.episode_number, season_number: season.season_number, title: ep.title, current_name: currentName, expected_name: expectedName, dest_path: orgEntry.dest_path, torrent_hash: orgHash, needs_rename: needsRename, file_exists: fileExists }) } } @@ -398,16 +372,8 @@ router.post('/rename-all', requireAuth, async (req, res) => { if (eps[String(ep.id)]) { orgEntry = eps[String(ep.id)]; orgHash = hash; break } } if (!orgEntry || !orgHash) continue - const currentName = orgEntry.dest_path ? path.basename(orgEntry.dest_path) : orgEntry.dest_filename - const srcExt = path.extname(currentName) - const { formatted_name: rawFmt, nfo_filename: rawNfo } = resolveEpNaming(ep, orgHash) - let expectedName: string - if (nfoSupport) { - expectedName = rawNfo ? rawNfo.replace(/\.[^.]+$/, '') + srcExt : currentName - } else { - expectedName = rawFmt?.trim() ? rawFmt.replace(/[<>:"/\\|?*]/g, '').trim() + srcExt : currentName - } - if (expectedName === currentName) continue + const { currentName, expectedName, needsRename } = computeExpectedName(ep, orgEntry, orgHash, nfoSupport) + if (!needsRename) continue const oldPath = orgEntry.dest_path const newPath = path.join(path.dirname(oldPath), expectedName) try {