feat: bouton télécharger et remplacer + fix extraction volumes UTF-8

- Ajout d'un bouton "télécharger et remplacer" avec popup de
  confirmation, qui passe tous les volumes du pack (pas seulement
  les manquants) et replace_existing=true à l'API.
- Nouvelle colonne replace_existing dans torrent_downloads.
- Fix critique du parseur de volumes : le pass 2 mélangeait les
  indices d'octets (String::find) avec les indices de caractères
  (Vec<char>), causant un décalage quand le titre contenait des
  caractères multi-octets (é, à...). "Tome #097" extrayait 9
  au lieu de 97. Réécrit en indexation char pure.
- Le préfixe "tome" skip désormais "#" (tome #097 → 97).
- Protection intra-batch : si une destination est déjà utilisée,
  le fichier garde son nom original au lieu d'écraser.
- Alerte WARN si N fichiers source donnent N/3 volumes uniques.
- Nettoyage du répertoire sl-{id} et de la catégorie qBittorrent
  après import.
- Badges volumes en flex-wrap dans la page downloads.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-27 16:01:08 +01:00
parent 9c18802864
commit eabb88eb9d
11 changed files with 205 additions and 66 deletions

View File

@@ -17,6 +17,9 @@ pub struct QBittorrentAddRequest {
pub library_id: Option<Uuid>,
pub series_name: Option<String>,
pub expected_volumes: Option<Vec<i32>>,
/// When true, overwrite existing files at destination during import.
#[serde(default)]
pub replace_existing: bool,
}
#[derive(Serialize, ToSchema)]
@@ -203,14 +206,15 @@ pub async fn add_torrent(
let id = download_id.unwrap();
sqlx::query(
"INSERT INTO torrent_downloads (id, library_id, series_name, expected_volumes, qb_hash) \
VALUES ($1, $2, $3, $4, $5)",
"INSERT INTO torrent_downloads (id, library_id, series_name, expected_volumes, qb_hash, replace_existing) \
VALUES ($1, $2, $3, $4, $5, $6)",
)
.bind(id)
.bind(library_id)
.bind(series_name)
.bind(expected_volumes)
.bind(qb_hash.as_deref())
.bind(body.replace_existing)
.execute(&state.pool)
.await?;