From 39d2f996aecb9f07dcd8c1ec7911090276908a2f Mon Sep 17 00:00:00 2001 From: MasutaYunikon Date: Wed, 29 Apr 2026 20:15:01 +0200 Subject: [PATCH] fix: spinner infinie apres supression --- server/routes/import.ts | 32 +++++++++++++++++++----- src/components/serie/SerieSeasonCard.vue | 11 ++++---- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/server/routes/import.ts b/server/routes/import.ts index 9933e87..632348f 100644 --- a/server/routes/import.ts +++ b/server/routes/import.ts @@ -8,6 +8,7 @@ import { readSettings } from '../settings.js' import { getGitlabTitle } from '../gitlab-map.js' import { readSerieData, loadEnrichedSeriesData } from '../lib/github-cache.js' import { GITLAB_API_NFO, GITLAB_RAW_NFO } from '../lib/nfo.js' +import { dispatchRemove } from '../torrent-clients/index.js' const router = Router() @@ -222,6 +223,7 @@ router.delete('/organized/:serieId', requireAuth, async (req, res) => { } let removed = 0 const errors: string[] = [] + const emptyHashes: string[] = [] for (const [hash, episodes] of Object.entries(organized)) { for (const epId of Object.keys(episodes)) { if (!episodeIds.has(epId)) continue @@ -232,10 +234,16 @@ router.delete('/organized/:serieId', requireAuth, async (req, res) => { delete organized[hash][epId] removed++ } - if (Object.keys(organized[hash]).length === 0) delete organized[hash] + if (Object.keys(organized[hash]).length === 0) { delete organized[hash]; emptyHashes.push(hash) } } saveOrganizedJson(organized) - logger.info('api', `Désimport série ${serieId} — ${removed} épisode(s) retirés`) + if (deleteFile) { + for (const hash of emptyHashes) { + if (hash === 'manual') continue + dispatchRemove(hash, false).catch(err => logger.warn('api', `Impossible de retirer le torrent ${hash.slice(0, 8)}… du client : ${err instanceof Error ? err.message : err}`)) + } + } + logger.info('api', `Désimport série ${serieId} — ${removed} épisode(s) retirés${deleteFile && emptyHashes.length ? `, ${emptyHashes.length} torrent(s) retirés du client` : ''}`) res.json({ ok: true, removed, errors }) }) @@ -252,6 +260,7 @@ router.delete('/organized/:serieId/seasons/:seasonId', requireAuth, async (req, const episodeIds = new Set(season.episodes?.map((e: any) => String(e.id)) ?? []) let removed = 0 const errors: string[] = [] + const emptyHashes: string[] = [] for (const [hash, episodes] of Object.entries(organized)) { for (const epId of Object.keys(episodes)) { if (!episodeIds.has(epId)) continue @@ -262,10 +271,16 @@ router.delete('/organized/:serieId/seasons/:seasonId', requireAuth, async (req, delete organized[hash][epId] removed++ } - if (Object.keys(organized[hash]).length === 0) delete organized[hash] + if (Object.keys(organized[hash]).length === 0) { delete organized[hash]; emptyHashes.push(hash) } } saveOrganizedJson(organized) - logger.info('api', `Désimport saison ${seasonId} (série ${serieId}) — ${removed} épisode(s) retirés`) + if (deleteFile) { + for (const hash of emptyHashes) { + if (hash === 'manual') continue + dispatchRemove(hash, false).catch(err => logger.warn('api', `Impossible de retirer le torrent ${hash.slice(0, 8)}… du client : ${err instanceof Error ? err.message : err}`)) + } + } + logger.info('api', `Désimport saison ${seasonId} (série ${serieId}) — ${removed} épisode(s) retirés${deleteFile && emptyHashes.length ? `, ${emptyHashes.length} torrent(s) retirés du client` : ''}`) res.json({ ok: true, removed, errors }) }) @@ -286,9 +301,14 @@ router.delete('/organized/:serieId/:episodeId', requireAuth, async (req, res) => logger.info('api', `Désimport + suppression fichier : "${foundEntry.dest_path}"`) } delete organized[foundHash][episodeId] - if (Object.keys(organized[foundHash]).length === 0) delete organized[foundHash] + const torrentEmpty = Object.keys(organized[foundHash]).length === 0 + if (torrentEmpty) delete organized[foundHash] saveOrganizedJson(organized) - logger.info('api', `Désimport ep ${episodeId}`) + // Si plus aucun épisode de ce torrent n'est importé → retirer le torrent du client + if (deleteFile && torrentEmpty && foundHash !== 'manual') { + dispatchRemove(foundHash, false).catch(err => logger.warn('api', `Impossible de retirer le torrent ${foundHash.slice(0, 8)}… du client : ${err instanceof Error ? err.message : err}`)) + } + logger.info('api', `Désimport ep ${episodeId}${deleteFile && torrentEmpty ? ` + torrent ${foundHash.slice(0, 8)}… retiré du client` : ''}`) res.json({ ok: true }) } catch (err) { const msg = err instanceof Error ? err.message : 'Erreur inconnue' diff --git a/src/components/serie/SerieSeasonCard.vue b/src/components/serie/SerieSeasonCard.vue index c3a1e9a..8476ec8 100644 --- a/src/components/serie/SerieSeasonCard.vue +++ b/src/components/serie/SerieSeasonCard.vue @@ -426,15 +426,16 @@ function epProgress(ep: any): ActiveTorrent | null { const sessionDl = isDownloaded(key0) || isDownloaded(keyI) if (active.files && active.files.length > 0) { const file = active.files.find(f => f.index === t.file_index) - // Avec données par fichier on se fie à la progression réelle du fichier précis, - // pas à l'état global du torrent — évite d'afficher les fichiers non sélectionnés (progress 0). - if (file != null && (sessionDl || file.progress > 0)) { + // Avec données par fichier on se fie à la progression réelle du fichier précis. + // Si le fichier est à 100% sans session active → déjà téléchargé hors session + // (ex : désimporté puis re-consulté) → ne pas afficher le spinner. + if (file != null && (sessionDl || (file.progress > 0 && file.progress < 100))) { return { ...active, progress: file.progress } } if (!sessionDl) continue } else { - // Pas de données par fichier : session ou torrent terminé - if (!sessionDl && active.state !== 'seeding') continue + // Pas de données par fichier : ignorer si pas de session active + if (!sessionDl) continue return { ...active, progress: active.state === 'seeding' ? 100 : active.progress } } }