feat: gestion des téléchargements qBittorrent avec import automatique

- Nouvelle table `torrent_downloads` pour suivre les téléchargements gérés
- API : endpoint POST /torrent-downloads/notify (webhook optionnel) et GET /torrent-downloads
- Poller background toutes les 30s qui interroge qBittorrent pour détecter
  les torrents terminés — aucune config "run external program" nécessaire
- Import automatique : déplacement des fichiers vers la série cible,
  renommage selon le pattern existant (détection de la largeur des digits),
  support packs multi-volumes, scan job déclenché après import
- Page /downloads dans le backoffice : filtres, auto-refresh, carte par download
- Toggle auto-import intégré dans la card qBittorrent des settings
- Erreurs de détection download affichées dans le détail des jobs
- Volume /downloads monté dans docker-compose

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-26 14:43:10 +01:00
parent a2de2e1601
commit 4bb142d1dd
21 changed files with 1197 additions and 39 deletions

View File

@@ -6,6 +6,7 @@ const en: Record<TranslationKey, string> = {
"nav.books": "Books",
"nav.series": "Series",
"nav.libraries": "Libraries",
"nav.downloads": "Downloads",
"nav.jobs": "Jobs",
"nav.tokens": "Tokens",
"nav.users": "Users",
@@ -401,6 +402,8 @@ const en: Record<TranslationKey, string> = {
"jobDetail.downloadAvailableReleases": "Available releases",
"jobDetail.downloadAvailableReleasesDesc": "{{count}} series with at least one release found",
"jobDetail.downloadMissingCount": "{{count}} missing",
"jobDetail.downloadErrors": "Detection errors",
"jobDetail.downloadErrorsDesc": "{{count}} series with errors during detection",
// Job types
"jobType.rebuild": "Indexing",
@@ -882,6 +885,25 @@ const en: Record<TranslationKey, string> = {
"series.metadataAll": "All",
"series.metadataLinked": "Linked",
"series.metadataUnlinked": "Not linked",
// Downloads page
"downloads.title": "Downloads",
"downloads.refresh": "Refresh",
"downloads.filterActive": "In progress",
"downloads.empty": "No downloads",
"downloads.volumes": "Volumes",
"downloads.filesImported": "files imported",
"downloads.status.downloading": "Downloading",
"downloads.status.completed": "Completed",
"downloads.status.importing": "Importing",
"downloads.status.imported": "Imported",
"downloads.status.error": "Error",
// Settings - Torrent Import
"settings.torrentImport": "Auto import",
"settings.torrentImportDesc": "When enabled, torrents added via the backoffice are tracked and files are automatically imported into the library when the download completes.",
"settings.torrentImportEnabled": "Enable auto import",
"settings.torrentImportPollingInfo": "The API polls qBittorrent every 30 seconds to detect completed downloads. No additional configuration in qBittorrent is required.",
};
export default en;

View File

@@ -4,6 +4,7 @@ const fr = {
"nav.books": "Livres",
"nav.series": "Séries",
"nav.libraries": "Bibliothèques",
"nav.downloads": "Téléchargements",
"nav.jobs": "Tâches",
"nav.tokens": "Jetons",
"nav.users": "Utilisateurs",
@@ -399,6 +400,8 @@ const fr = {
"jobDetail.downloadAvailableReleases": "Releases disponibles",
"jobDetail.downloadAvailableReleasesDesc": "{{count}} série(s) avec au moins une release trouvée",
"jobDetail.downloadMissingCount": "{{count}} manquant(s)",
"jobDetail.downloadErrors": "Erreurs de détection",
"jobDetail.downloadErrorsDesc": "{{count}} série(s) en erreur lors de la détection",
// Job types
"jobType.rebuild": "Indexation",
@@ -880,6 +883,25 @@ const fr = {
"series.metadataAll": "Toutes",
"series.metadataLinked": "Associée",
"series.metadataUnlinked": "Non associée",
// Downloads page
"downloads.title": "Téléchargements",
"downloads.refresh": "Actualiser",
"downloads.filterActive": "En cours",
"downloads.empty": "Aucun téléchargement",
"downloads.volumes": "Volumes",
"downloads.filesImported": "fichiers importés",
"downloads.status.downloading": "Téléchargement",
"downloads.status.completed": "Terminé",
"downloads.status.importing": "Import en cours",
"downloads.status.imported": "Importé",
"downloads.status.error": "Erreur",
// Settings - Torrent Import
"settings.torrentImport": "Import automatique",
"settings.torrentImportDesc": "Lorsqu'activé, les torrents ajoutés via le backoffice sont suivis et les fichiers sont automatiquement importés dans la bibliothèque à la fin du téléchargement.",
"settings.torrentImportEnabled": "Activer l'import automatique",
"settings.torrentImportPollingInfo": "L'API interroge qBittorrent toutes les 30 secondes pour détecter les téléchargements terminés. Aucune configuration supplémentaire dans qBittorrent n'est nécessaire.",
} as const;
export type TranslationKey = keyof typeof fr;