From ed556acd1d2990a6375d4a60f745048a79854249 Mon Sep 17 00:00:00 2001 From: MasutaYunikon Date: Thu, 28 May 2026 14:49:30 +0200 Subject: [PATCH] =?UTF-8?q?fix(torrent):=20correction=20suppression=20torr?= =?UTF-8?q?ent=20apr=C3=A8s=20d=C3=A9placement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - synology-ds: ajout cache _hashToSynoId (hash BT → dbid_X) peuplé dans list(), utilisé dans remove() et getFiles() pour retrouver la tâche même quand detail.hash et detail.uri sont vides dans la réponse DSM - index: contrôle du retour de dispatchRemove et log d'erreur explicite si la suppression échoue, au lieu de toujours logger "supprimé" --- server/index.ts | 8 ++++---- server/torrent-clients/synology-ds.ts | 29 +++++++++++++++++++++++---- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/server/index.ts b/server/index.ts index 87b11ad..8b7765f 100644 --- a/server/index.ts +++ b/server/index.ts @@ -210,11 +210,11 @@ server.listen(PORT, async () => { } const { organizeMode, deleteTorrentOnMove } = readSettings() if (organizeMode === 'move' && deleteTorrentOnMove && result.done > 0) { - try { - await dispatchRemove(result.hash, false) + const removeResult = await dispatchRemove(result.hash, false) + if (removeResult.ok) { logger.info('api', `Torrent "${result.name}" supprimé après move`) - } catch (err) { - logger.warn('api', `Impossible de supprimer le torrent "${result.name}" : ${err instanceof Error ? err.message : err}`) + } else { + logger.warn('api', `Torrent "${result.name}" — suppression échouée après move : ${removeResult.error ?? 'aucun client n\'a pu supprimer'}`) } } } diff --git a/server/torrent-clients/synology-ds.ts b/server/torrent-clients/synology-ds.ts index 6780985..4d99ba2 100644 --- a/server/torrent-clients/synology-ds.ts +++ b/server/torrent-clients/synology-ds.ts @@ -15,6 +15,13 @@ import { DATA_DIR } from '../config.js' const URI_HASH_PATH = path.join(DATA_DIR, 'synology_uri_hash.json') +// ─── Cache hash BT → ID Synology (en mémoire) ───────────────────────────────── +// Synology utilise des IDs internes (dbid_X), pas des infohash BT. +// Quand list() réussit à résoudre le vrai hash BT, on mémorise l'association +// infohash → dbid_X pour que remove() / getFiles() puissent retrouver la tâche +// même quand detail.hash et detail.uri sont vides. +const _hashToSynoId = new Map() + function loadUriHashMap(): Record { try { if (fs.existsSync(URI_HASH_PATH)) return JSON.parse(fs.readFileSync(URI_HASH_PATH, 'utf-8')) @@ -178,6 +185,12 @@ const DS: TorrentClientDriver = { String(t.id ?? ''), ) + // Mémoriser l'association hash BT → ID Synology (pour remove/getFiles) + const synoIdStr = String(t.id ?? '').toLowerCase() + if (!hash.startsWith('dbid_') && synoIdStr) { + _hashToSynoId.set(hash, synoIdStr) + } + const files = fileList.length > 0 ? fileList.map((f: any, i: number) => { const fSize = typeof f.size === 'number' ? f.size : 0 @@ -262,13 +275,17 @@ const DS: TorrentClientDriver = { { additional: 'detail,file' }, sid) const tasks: any[] = data?.tasks ?? [] - const h = hash.toLowerCase() + const h = hash.toLowerCase() + const synoIdFromHash = _hashToSynoId.get(h) ?? '' const task = tasks.find((t: any) => { const detailHash = String(t.additional?.detail?.hash ?? '').toLowerCase() const detailUri = String(t.additional?.detail?.uri ?? '').toLowerCase() const synoId = String(t.id ?? '').toLowerCase() const cached = _uriToHash.get(detailUri) ?? '' - return detailHash === h || synoId === h || cached === h + return detailHash === h + || synoId === h + || cached === h + || (synoIdFromHash && synoId === synoIdFromHash) }) if (!task) return [] @@ -290,13 +307,17 @@ const DS: TorrentClientDriver = { const data = await dsRequest(config, 'SYNO.DownloadStation.Task', 'list', '1', { additional: 'detail' }, sid) const tasks: any[] = data?.tasks ?? [] - const h = hash.toLowerCase() + const h = hash.toLowerCase() + const synoIdFromHash = _hashToSynoId.get(h) ?? '' const found = tasks.find((t: any) => { const detailHash = String(t.additional?.detail?.hash ?? '').toLowerCase() const detailUri = String(t.additional?.detail?.uri ?? '').toLowerCase() const synoId = String(t.id ?? '').toLowerCase() const cachedHash = _uriToHash.get(detailUri) ?? '' - return detailHash === h || synoId === h || cachedHash === h + return detailHash === h + || synoId === h + || cachedHash === h + || (synoIdFromHash && synoId === synoIdFromHash) }) if (!found) throw new Error(`Torrent ${hash.slice(0, 8)}… introuvable`)