feat(client): ajour de alldebrid client
fix(log): changement pour une notification plus explicit lors du test client fix(import): supprime les entrees organiser quand on supprime un fichier fix(start): url cliquable au demarrage dans le terminal
This commit is contained in:
1 parent
c9040ea68c
commit
6ce59b9e04
3 files changed
+36
-3
No files matched your search
+5
-1
@@ -12,6 +12,7 @@ import synologyDsDriver from './torrent-clients/synology-ds.js'
|
||||
import utorrentDriver from './torrent-clients/utorrent.js'
|
||||
import rtorrentDriver from './torrent-clients/rtorrent.js'
|
||||
import realDebridDriver from './torrent-clients/real-debrid.js'
|
||||
import allDebridDriver from './torrent-clients/alldebrid.js'
|
||||
import { autoOrganizeAll, scanMediaPath, syncFilenameChanges, migrateOrganizedEpisodeIds } from './organize.js'
|
||||
import { logger } from './logger.js'
|
||||
import { DATA_DIR, BASE_DIR } from './config.js'
|
||||
@@ -37,6 +38,7 @@ registerDriver(synologyDsDriver)
|
||||
registerDriver(utorrentDriver)
|
||||
registerDriver(rtorrentDriver)
|
||||
registerDriver(realDebridDriver)
|
||||
registerDriver(allDebridDriver)
|
||||
|
||||
const app = express()
|
||||
const PORT = Number(process.env.PORT) || 9898
|
||||
@@ -76,7 +78,9 @@ if (fs.existsSync(PUBLIC_PATH)) {
|
||||
const server = http.createServer({ maxHeaderSize: 32768 }, app)
|
||||
|
||||
server.listen(PORT, async () => {
|
||||
logger.info('api', `Serveur démarré sur le port ${PORT}`)
|
||||
const url = `http://localhost:${PORT}`
|
||||
console.log(`\n FanKarr → \x1b]8;;${url}\x1b\\${url}\x1b]8;;\x1b\\\n`)
|
||||
logger.info('api', `Serveur démarré — ${url}`)
|
||||
try {
|
||||
const available = await readAvailable()
|
||||
logger.info('api', `Cache GitHub initialisé — ${available.length} séries disponibles`)
|
||||
|
||||
@@ -89,6 +89,22 @@ router.post('/manual-import', requireAuth, async (req, res) => {
|
||||
if (file_path !== destPath) {
|
||||
const serieRootPath = path.join(mediaPath, serieTitle)
|
||||
const isInSeriePath = file_path.startsWith(serieRootPath + path.sep) || file_path.startsWith(serieRootPath + '/')
|
||||
// Si une ancienne version de cet épisode existe à la destination (réassignation),
|
||||
// la supprimer avant le rename pour éviter un état incohérent.
|
||||
if (isInSeriePath && fs.existsSync(destPath) && destPath !== file_path) {
|
||||
fs.unlinkSync(destPath)
|
||||
logger.info('api', `Import manuel (réassignation) : suppression ancien fichier "${destFilename}"`)
|
||||
// Nettoyer toute entrée organized qui pointait sur ce fichier supprimé
|
||||
for (const [h, eps] of Object.entries(organized)) {
|
||||
for (const [epId, entry] of Object.entries(eps)) {
|
||||
if (entry?.dest_path === destPath) {
|
||||
delete (organized[h] as any)[epId]
|
||||
logger.info('api', `Import manuel : suppression entrée stale ep ${epId} (ancien fichier écrasé)`)
|
||||
}
|
||||
}
|
||||
if (Object.keys(organized[h]).length === 0) delete organized[h]
|
||||
}
|
||||
}
|
||||
if (isInSeriePath && !fs.existsSync(destPath)) {
|
||||
fs.renameSync(file_path, destPath)
|
||||
logger.info('api', `Import manuel (rename) : "${srcFilename}" → "${destFilename}"`)
|
||||
|
||||
@@ -6,6 +6,17 @@
|
||||
import type { TorrentClientDriver, TorrentInfo, DownloadOptions } from './index.js'
|
||||
import { logger } from '../logger.js'
|
||||
|
||||
// qBittorrent répond normalement "Ok." mais certains setups (reverse proxy, etc.)
|
||||
// peuvent wrapper la réponse en JSON — on accepte les deux
|
||||
function isQbAddSuccess(text: string): boolean {
|
||||
if (text.trim() === 'Ok.') return true
|
||||
try {
|
||||
const json = JSON.parse(text)
|
||||
if (typeof json?.success_count === 'number') return json.success_count > 0
|
||||
} catch {}
|
||||
return false
|
||||
}
|
||||
|
||||
function mapState(state: string): TorrentInfo['state'] {
|
||||
if (['downloading', 'metaDL', 'queuedDL', 'stalledDL', 'forcedDL'].includes(state)) return 'downloading'
|
||||
if (['uploading', 'queuedUP', 'stalledUP', 'forcedUP'].includes(state)) return 'seeding'
|
||||
@@ -37,6 +48,7 @@ async function qbLogin(config: Record<string, string | number>): Promise<string>
|
||||
|
||||
const text = await res.text()
|
||||
if (!res.ok) throw new Error(`Login échoué : ${text.trim() || res.status}`)
|
||||
if (text.trim() === 'Fails.') throw new Error('Identifiants incorrects (vérifiez le nom d\'utilisateur et le mot de passe)')
|
||||
|
||||
const cookie = (res.headers.get('set-cookie') ?? '').split(';')[0].trim()
|
||||
if (!cookie) throw new Error('Cookie de session introuvable')
|
||||
@@ -165,6 +177,7 @@ const QB: TorrentClientDriver = {
|
||||
})
|
||||
if (!res.ok) return { online: false }
|
||||
const version = (await res.text()).trim()
|
||||
if (version.startsWith('<')) throw new Error('Réponse HTML reçue — clé API invalide ou non supportée par cette version de qBittorrent')
|
||||
logger.debug('qbittorrent', `Healthcheck OK — version ${version}`)
|
||||
return { online: true, version }
|
||||
} catch (err) {
|
||||
@@ -296,7 +309,7 @@ const QB: TorrentClientDriver = {
|
||||
res = await fetch(`${config.url}/api/v2/torrents/add`, { method: 'POST', body: form, headers: authH })
|
||||
}
|
||||
const text = await res.text()
|
||||
if (text !== 'Ok.') throw new Error(`Ajout échoué : ${text}`)
|
||||
if (!isQbAddSuccess(text)) throw new Error(`Ajout échoué : ${text}`)
|
||||
|
||||
if (hash) {
|
||||
logger.info('qbittorrent', `Torrent ajouté — sélection fichier ${options.file_index} en attente des métadonnées`)
|
||||
@@ -339,7 +352,7 @@ const QB: TorrentClientDriver = {
|
||||
headers: { ...authH, 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
})
|
||||
const text = await res.text()
|
||||
if (text !== 'Ok.') throw new Error(`Ajout échoué : ${text}`)
|
||||
if (!isQbAddSuccess(text)) throw new Error(`Ajout échoué : ${text}`)
|
||||
logger.info('qbittorrent', `Torrent ajouté (catégorie: ${config.category ?? 'aucune'}${config.savePath ? `, dossier: ${config.savePath}` : ''})`)
|
||||
},
|
||||
|
||||
|
||||
Reference in new issue
Block a user