feat: ajout du bouton suppresion de torrent en cours
feat: ajout d'un bouton supprimé l'importation dans le menu d'importation d'une serie feat: ajout de badge quand le fichier est introuvable feat: suppression des series supprimé feat: bouton tous télécharger sur les saisons et series
This commit is contained in:
1 parent
5e79699ed9
commit
d6f4433541
5 files changed
+487
-499
No files matched your search
+49
-9
@@ -54,16 +54,17 @@ app.get('/api/settings', requireAuth, (_req, res) => {
|
||||
res.json(readSettings())
|
||||
})
|
||||
app.post('/api/settings', requireAuth, (req, res) => {
|
||||
const { mediaPath, completePath, organizeMode, category, nfoSupport, autoImport, devMode, deleteTorrentOnMove } = req.body
|
||||
const { mediaPath, completePath, organizeMode, category, nfoSupport, autoImport, devMode, deleteTorrentOnMove, autoUnimportMissing } = req.body
|
||||
const patch: Record<string, any> = {}
|
||||
if (mediaPath !== undefined) patch.mediaPath = mediaPath
|
||||
if (completePath !== undefined) patch.completePath = completePath
|
||||
if (organizeMode !== undefined) patch.organizeMode = organizeMode
|
||||
if (category !== undefined) patch.category = category
|
||||
if (nfoSupport !== undefined) patch.nfoSupport = nfoSupport
|
||||
if (autoImport !== undefined) patch.autoImport = autoImport
|
||||
if (devMode !== undefined) patch.devMode = devMode
|
||||
if (deleteTorrentOnMove !== undefined) patch.deleteTorrentOnMove = deleteTorrentOnMove
|
||||
if (mediaPath !== undefined) patch.mediaPath = mediaPath
|
||||
if (completePath !== undefined) patch.completePath = completePath
|
||||
if (organizeMode !== undefined) patch.organizeMode = organizeMode
|
||||
if (category !== undefined) patch.category = category
|
||||
if (nfoSupport !== undefined) patch.nfoSupport = nfoSupport
|
||||
if (autoImport !== undefined) patch.autoImport = autoImport
|
||||
if (devMode !== undefined) patch.devMode = devMode
|
||||
if (deleteTorrentOnMove !== undefined) patch.deleteTorrentOnMove = deleteTorrentOnMove
|
||||
if (autoUnimportMissing !== undefined) patch.autoUnimportMissing = autoUnimportMissing
|
||||
const updated = writeSettings(patch)
|
||||
logger.info('api', 'Paramètres mis à jour')
|
||||
res.json(updated)
|
||||
@@ -840,6 +841,43 @@ app.post('/api/rename-episode', requireAuth, async (req, res) => {
|
||||
}
|
||||
})
|
||||
|
||||
// ── Désimport série complète ───────────────────────────────────
|
||||
app.delete('/api/organized/:serieId', requireAuth, async (req, res) => {
|
||||
const serieId = String(req.params.serieId)
|
||||
const deleteFile = req.query.deleteFile === 'true'
|
||||
const ORGANIZED_PATH = path.join(DATA_DIR, 'organized.json')
|
||||
let organized: Record<string, Record<string, any>> = {}
|
||||
try { if (fs.existsSync(ORGANIZED_PATH)) organized = JSON.parse(fs.readFileSync(ORGANIZED_PATH, 'utf-8')) } catch {}
|
||||
|
||||
const sd = await readSerieData(Number(serieId))
|
||||
if (!sd) { res.status(404).json({ error: 'Série introuvable' }); return }
|
||||
|
||||
const episodeIds = new Set<string>()
|
||||
for (const season of sd.seasons ?? []) {
|
||||
for (const ep of season.episodes ?? []) episodeIds.add(String(ep.id))
|
||||
}
|
||||
|
||||
let removed = 0
|
||||
const errors: string[] = []
|
||||
|
||||
for (const [hash, episodes] of Object.entries(organized)) {
|
||||
for (const epId of Object.keys(episodes)) {
|
||||
if (!episodeIds.has(epId)) continue
|
||||
const entry = episodes[epId]
|
||||
if (deleteFile && entry?.dest_path && fs.existsSync(entry.dest_path)) {
|
||||
try { fs.unlinkSync(entry.dest_path) } catch (err) { errors.push(entry.dest_path) }
|
||||
}
|
||||
delete organized[hash][epId]
|
||||
removed++
|
||||
}
|
||||
if (Object.keys(organized[hash]).length === 0) delete organized[hash]
|
||||
}
|
||||
|
||||
fs.writeFileSync(ORGANIZED_PATH, JSON.stringify(organized, null, 2), 'utf-8')
|
||||
logger.info('api', `Désimport série ${serieId} — ${removed} épisode(s) retirés`)
|
||||
res.json({ ok: true, removed, errors })
|
||||
})
|
||||
|
||||
// ── Désimport épisode ──────────────────────────────────────────
|
||||
app.delete('/api/organized/:serieId/:episodeId', requireAuth, async (req, res) => {
|
||||
const serieId = String(req.params.serieId)
|
||||
@@ -921,6 +959,7 @@ app.get('/api/organized-summary', requireAuth, async (_req, res) => {
|
||||
}
|
||||
|
||||
const needsRename = expectedName !== currentName
|
||||
const fileExists = orgEntry.dest_path ? fs.existsSync(orgEntry.dest_path) : false
|
||||
episodes.push({
|
||||
episode_id : ep.id,
|
||||
episode_number: ep.episode_number,
|
||||
@@ -931,6 +970,7 @@ app.get('/api/organized-summary', requireAuth, async (_req, res) => {
|
||||
dest_path : orgEntry.dest_path,
|
||||
torrent_hash : orgHash,
|
||||
needs_rename : needsRename,
|
||||
file_exists : fileExists,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+19
-2
@@ -196,11 +196,28 @@ export async function scanMediaPath(
|
||||
if (Object.keys(organized[hash]).length === 0) delete organized[hash]
|
||||
}
|
||||
|
||||
if (result.added > 0 || removed > 0) {
|
||||
// Désimport automatique des fichiers manquants sur le disque
|
||||
const { autoUnimportMissing } = readSettings() as any
|
||||
let autoRemoved = 0
|
||||
if (autoUnimportMissing) {
|
||||
for (const [hash, episodes] of Object.entries(organized)) {
|
||||
for (const [episodeId, entry] of Object.entries(episodes as Record<string, any>)) {
|
||||
const destPath = entry?.dest_path
|
||||
if (destPath && !fs.existsSync(destPath)) {
|
||||
delete (organized[hash] as any)[episodeId]
|
||||
autoRemoved++
|
||||
logger.info('organize', `Désimport auto : fichier introuvable "${destPath}"`)
|
||||
}
|
||||
}
|
||||
if (Object.keys(organized[hash]).length === 0) delete organized[hash]
|
||||
}
|
||||
}
|
||||
|
||||
if (result.added > 0 || removed > 0 || autoRemoved > 0) {
|
||||
fs.writeFileSync(organizedPath, JSON.stringify(organized, null, 2), 'utf-8')
|
||||
}
|
||||
|
||||
logger.info('organize', `Scan terminé — ${result.found} fichiers, ${result.added} ajoutés, ${removed} orphelins supprimés${noMatch > 0 ? `, ${noMatch} non matchés` : ''}`)
|
||||
logger.info('organize', `Scan terminé — ${result.found} fichiers, ${result.added} ajoutés, ${removed} orphelins supprimés${autoRemoved > 0 ? `, ${autoRemoved} désimportés auto (fichier manquant)` : ''}${noMatch > 0 ? `, ${noMatch} non matchés` : ''}`)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user