fix: les torrents sont stopé lors de la selection de fichier pour eviter qu'il commence le telechargement des autres fichiers avant de set les indexs

This commit is contained in:
MasutaYunikon committed 2026-04-21 15:30:00 +02:00
1 parent f19cc027ee
commit b3ff17ab11
3 files changed
+37 -41

No files matched your search

+14 -11
View File
@@ -48,9 +48,9 @@ async function qbTorrentExists(config: Record<string, string | number>, sid: str
}
/**
* Attend les métadonnées, applique les priorités de fichiers, puis reprend le torrent si `resume=true`.
* - resume=false : torrent déjà actif, on met juste à jour la priorité
* - resume=true : torrent ajouté en pause, on le reprend après avoir défini les priorités
* Attend que les métadonnées soient disponibles, applique les priorités, puis reprend le torrent.
* Utilisé après un ajout avec stopCondition=MetadataReceived (qBit actif → auto-pause après metadata)
* ou pour un torrent déjà présent (resume=false, juste mise à jour de priorité).
*/
async function qbApplyFilePriority(
config : Record<string, string | number>,
@@ -58,8 +58,9 @@ async function qbApplyFilePriority(
fileIndex: number,
resume : boolean,
): Promise<void> {
for (let attempt = 0; attempt < 30; attempt++) {
await new Promise(r => setTimeout(r, 1000))
// 60 tentatives × 500 ms = 30 s max
for (let attempt = 0; attempt < 60; attempt++) {
await new Promise(r => setTimeout(r, 500))
try {
const sid = await qbLogin(config)
const res = await fetch(`${config.url}/api/v2/torrents/files?hash=${hash}`, {
@@ -86,7 +87,7 @@ async function qbApplyFilePriority(
form2.append('priority', '1')
await fetch(`${config.url}/api/v2/torrents/filePrio`, { method: 'POST', body: form2, headers: { Cookie: `SID=${sid}` } })
// Reprendre si le torrent était en pause
// Reprendre (torrent auto-mis en pause par stopCondition=MetadataReceived)
if (resume) {
const resumeForm = new FormData()
resumeForm.append('hashes', hash)
@@ -98,7 +99,7 @@ async function qbApplyFilePriority(
} catch {}
}
// Timeout : reprendre quand même pour ne pas laisser le torrent bloqué en pause
// Timeout : reprendre quand même pour ne pas laisser le torrent bloqué
if (resume) {
try {
const sid = await qbLogin(config)
@@ -198,13 +199,15 @@ const QB: TorrentClientDriver = {
return
}
// Nouveau torrent → ajout en pause pour éviter tout téléchargement avant la sélection
// Nouveau torrent → ajout actif avec stopCondition=MetadataReceived
// qBit fetch les métadonnées normalement via DHT/peers,
// puis se met en pause automatiquement dès qu'elles sont disponibles.
// On set les priorités sur la pause, puis on reprend. Zéro data indésirable.
const form = new FormData()
form.append('urls', url)
if (config.category) form.append('category', String(config.category))
if (config.savePath) form.append('savepath', String(config.savePath))
form.append('paused', 'true') // qBit < 5.0
form.append('stopped', 'true') // qBit ≥ 5.0
form.append('stopCondition', 'MetadataReceived') // qBit ≥ 4.6
const res = await fetch(`${config.url}/api/v2/torrents/add`, {
method: 'POST', body: form, headers: { Cookie: `SID=${sid}` },
@@ -212,7 +215,7 @@ const QB: TorrentClientDriver = {
const text = await res.text()
if (text !== 'Ok.') throw new Error(`Ajout échoué : ${text}`)
logger.info('qbittorrent', `Torrent ajouté en pause (sélection fichier ${options.file_index} en attente de métadonnées)`)
logger.info('qbittorrent', `Torrent ajouté (stopCondition=MetadataReceived, sélection fichier ${options.file_index} en attente)`)
qbApplyFilePriority(config, hash, options.file_index, true).catch(err =>
logger.warn('qbittorrent', `Priorité fichier non appliquée : ${err instanceof Error ? err.message : err}`)
)
+16 -23
View File
@@ -128,18 +128,19 @@ async function rtTorrentExists(config: Record<string, string | number>, hash: st
}
/**
* Attend les métadonnées, applique les priorités, puis démarre le torrent si `start=true`.
* - start=false : torrent déjà actif, on met juste à jour la priorité
* - start=true : torrent chargé sans démarrer (load.normal), on le démarre après les priorités
* Attend les métadonnées via polling rapide (300 ms), applique les priorités.
* Le torrent est ajouté avec load.start — il fetch les métadonnées activement via DHT/peers.
* Pendant le fetch (état "métadonnées en cours"), aucun byte de fichier n'est téléchargé.
* On set les priorités dès que les fichiers sont disponibles, avant que le download réel commence.
*/
async function rtApplyFilePriority(
config : Record<string, string | number>,
hash : string,
fileIndex: number,
start : boolean,
): Promise<void> {
for (let attempt = 0; attempt < 30; attempt++) {
await new Promise(r => setTimeout(r, 1000))
// 100 tentatives × 300 ms = 30 s max
for (let attempt = 0; attempt < 100; attempt++) {
await new Promise(r => setTimeout(r, 300))
try {
const files: any[] = await rpcCall(config, 'f.multicall', [hash, '', 'f.size_bytes='])
const fileCount = Array.isArray(files) ? files.length : 0
@@ -149,22 +150,12 @@ async function rtApplyFilePriority(
await rpcCall(config, 'f.priority.set', [hash, i, i === fileIndex ? 1 : 0])
}
if (start) {
await rpcCall(config, 'd.start', [hash])
}
logger.info('rtorrent', `Fichier ${fileIndex} sélectionné${start ? ' + démarrage' : ''} pour ${hash.slice(0, 8)}…`)
logger.info('rtorrent', `Fichier ${fileIndex} sélectionné pour ${hash.slice(0, 8)}…`)
return
} catch {}
}
// Timeout : démarrer quand même pour ne pas laisser le torrent bloqué
if (start) {
try { await rpcCall(config, 'd.start', [hash]) } catch {}
logger.warn('rtorrent', `Timeout : démarré sans sélection de fichier pour ${hash.slice(0, 8)}…`)
} else {
throw new Error(`Timeout : métadonnées non disponibles pour ${hash.slice(0, 8)}…`)
}
throw new Error(`Timeout : métadonnées non disponibles pour ${hash.slice(0, 8)}…`)
}
// ─── State mapping ────────────────────────────────────────────
@@ -279,20 +270,22 @@ const RT: TorrentClientDriver = {
if (exists) {
// Torrent déjà présent → mettre à jour la priorité directement (async OK)
logger.info('rtorrent', `Torrent ${hash.slice(0, 8)}… déjà présent, mise à jour priorité fichier ${options.file_index}`)
rtApplyFilePriority(config, hash, options.file_index, false).catch(err =>
rtApplyFilePriority(config, hash, options.file_index).catch(err =>
logger.warn('rtorrent', `Priorité fichier non appliquée : ${err instanceof Error ? err.message : err}`)
)
return
}
// Nouveau torrent → charger sans démarrer pour éviter tout téléchargement avant la sélection
// Nouveau torrent → load.start (fetch métadonnées via DHT/peers actif)
// Pendant le fetch des métadonnées, aucun byte de fichier n'est téléchargé.
// On poll à 300 ms et on set les priorités dès que les fichiers sont disponibles.
const args: any[] = ['', url]
if (config.savePath) args.push(`d.directory.set="${config.savePath}"`)
if (config.category) args.push(`d.custom1.set=${config.category}`)
await rpcCall(config, 'load.normal', args)
await rpcCall(config, 'load.start', args)
logger.info('rtorrent', `Torrent chargé sans démarrage (sélection fichier ${options.file_index} en attente de métadonnées)`)
rtApplyFilePriority(config, hash, options.file_index, true).catch(err =>
logger.info('rtorrent', `Torrent ajouté (sélection fichier ${options.file_index} en attente de métadonnées)`)
rtApplyFilePriority(config, hash, options.file_index).catch(err =>
logger.warn('rtorrent', `Priorité fichier non appliquée : ${err instanceof Error ? err.message : err}`)
)
return
+7 -7
View File
@@ -78,8 +78,8 @@
</table>
</div>
<p class="text-[11px] text-muted mt-2">
<span class="text-green-400">✓ Natif</span> — appliqué à l'ajout, aucun fichier superflu téléchargé ·
<span class="text-yellow-500">Async (pause)</span> — ajout en pause, sélection après métadonnées, puis reprise ·
<span class="text-green-400">✓ Natif</span> — sélection à l'ajout, aucun fichier superflu ·
<span class="text-green-400">Async ✓</span> — sélection dès réception des métadonnées, avant tout téléchargement ·
<span class="text-muted">✗</span> — non supporté, torrent complet téléchargé
</p>
</div>
@@ -163,11 +163,11 @@ const availableClients = ref<any[]>([])
const healthStatus = ref<Record<string, boolean | null>>({})
const clientMatrix = [
{ id: 'qbittorrent', label: 'qBittorrent', fileSelect: 'Async (pause)', categories: true },
{ id: 'transmission', label: 'Transmission', fileSelect: '✓ Natif', categories: true },
{ id: 'rtorrent', label: 'rTorrent / ruTorrent', fileSelect: 'Async (pause)', categories: true },
{ id: 'utorrent', label: 'uTorrent', fileSelect: '✗', categories: true },
{ id: 'synology-ds', label: 'Synology Download Station', fileSelect: '✗', categories: false },
{ id: 'qbittorrent', label: 'qBittorrent', fileSelect: 'Async ✓', categories: true },
{ id: 'transmission', label: 'Transmission', fileSelect: '✓ Natif', categories: true },
{ id: 'rtorrent', label: 'rTorrent / ruTorrent', fileSelect: 'Async ✓', categories: true },
{ id: 'utorrent', label: 'uTorrent', fileSelect: '✗', categories: true },
{ id: 'synology-ds', label: 'Synology Download Station', fileSelect: '✗', categories: false },
]
const modal = ref({