Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 

Repository files navigation

# qBittorrent Dedupe & Hardlink Manager

Script Bash intelligent pour **qBittorrent** qui analyse automatiquement les torrents, détecte leurs liens avec les bibliothèques **Radarr/Sonarr**, gère le **cross-seed** et applique une classification par **tags** directement dans l'interface qBittorrent.

## Fonctionnalites

- **Multi-instance** : gere simultanement plusieurs instances qBittorrent (ex: VPN + Direct)
- **Detection automatique des liens** : repere les torrents deja presents dans les bibliotheques Arr via les inodes (hardlinks)
- **Cross-seed** : identifie les torrents lies a un meme fichier media via plusieurs sources
- **Classification par tags** : `linked`, `cross-linked`, `partial`, `no-media`, `orphan`, `a effacer`
- **Reparation automatique** (optionnel) : cree des hardlinks manuels pour les torrents orphelins quand un fichier identique existe dans les medias
- **Duree de seed minimale par tracker** : verifie si la duree de seed requise est atteinte avant de marquer un torrent a supprimer
- **Orphelins de disque** : scanne les repertoires medias pour detecter les fichiers qui ne sont ni geres par les Arr, ni lies a un torrent
- **Caches resilients** : systeme de cache disque pour les inodes, hashes et statuts, avec reprise sur interruption (Ctrl+C)

## Prerequis

- **Bash** >= 4.0 (tableaux associatifs)
- **curl** — API qBittorrent / Radarr / Sonarr
- **python3** — parsing JSON
- **xxhash** ou **md5sum** — comparaison de contenu pour la reparation
- Acces reseau aux instances qBittorrent et aux APIs Arr

## Installation

```bash
# 1. Creer le dossier de travail
mkdir -p ~/qb-dedupe && cd ~/qb-dedupe

# 2. Creer le dossier de configuration
mkdir -p cleanup

# 3. Copier le script
chmod +x check_hardlinks.sh

# 4. Editer la configuration
nano cleanup/cleanup.conf

Configuration (cleanup.conf)

Instances qBittorrent

# Liste des instances a surveiller
INSTANCES=("VPN" "DIRECT")

# Instance VPN (ex: container derriere un VPN)
QBIT_VPN_URL="http://192.168.1.50:8080"
QBIT_VPN_USER="admin"
QBIT_VPN_PASS="votre_mot_de_passe"

# Instance Direct (ex: container direct)
QBIT_DIRECT_URL="http://192.168.1.51:8080"
QBIT_DIRECT_USER="admin"
QBIT_DIRECT_PASS="votre_mot_de_passe"

Repertoires medias

Chemins sur l'hote (pas les chemins Docker internes) :

MEDIA_DIRS=(
    "/mnt/tank/medias/films"
    "/mnt/tank/medias/series"
)

Translation Docker -> Hote

Si qBittorrent tourne dans un container et voit /data/completed alors que l'hote voit /mnt/tank/torrents :

declare -A PATH_MAP
PATH_MAP["/data/completed"]="/mnt/tank/torrents"
PATH_MAP["/downloads"]="/mnt/tank/downloads"

Radarr / Sonarr

Syntaxe obligatoire : cles composites INSTANCE|app pour eviter l'ecrasement des valeurs dans le tableau associatif bash.

# INCORRECT (la derniere ligne ecrase la premiere)
# ARR_CONFIG["VPN"]="radarr|..."
# ARR_CONFIG["VPN"]="sonarr|..."

# CORRECT
declare -A ARR_CONFIG
ARR_CONFIG["VPN|radarr"]="https://radarr.example.com|VOTRE_CLE_API"
ARR_CONFIG["VPN|sonarr"]="https://sonarr.example.com|VOTRE_CLE_API"
ARR_CONFIG["DIRECT|radarr"]="https://radarr.example.com|VOTRE_CLE_API"
ARR_CONFIG["DIRECT|sonarr"]="https://sonarr.example.com|VOTRE_CLE_API"

Tags

Les noms doivent correspondre exactement aux tags crees dans l'UI qBittorrent.

TAG_LINKED="linked"
TAG_CROSS_LINKED="cross-linked"
TAG_PARTIAL="partial"
TAG_NO_MEDIA="no-media"
TAG_ORPHAN="orphan"
TAG_DELETE="a effacer"

# Tags a supprimer AVANT d'appliquer les nouveaux (nettoyage)
DELETE_TAGS=("linked" "cross-linked" "partial" "no-media" "orphan" "a effacer")

Options avancees

Variable Description Defaut
AUTO_REPAIR Active la Phase 5 (hardlinks manuels) false
ARR_CACHE_DURATION Duree de vie du cache Arr (secondes). 0 = desactive 3600
SCAN_DISK_ORPHANS Active le scan des fichiers non references false
DISK_ORPHAN_LOG Chemin du rapport des orphelins de disque disk_orphans.log
DISK_ORPHAN_MIN_SIZE Taille minimale (octets) pour les orphelins de disque 0
CHOWN_FILES Applique un chown sur les nouveaux hardlinks false
CHOWN_USER Utilisateur:groupe pour le chown mich:users

Comment ca marche

Le script s'execute en 6 phases :

Phase 0 — Connexion et collecte

Connexion aux instances qBittorrent via l'API et recuperation de la liste des torrents.

Phase 1 — Inodes des Arr

Interrogation des APIs Radarr (/api/v3/movie) et Sonarr (/api/v3/series puis /api/v3/episodefile?seriesId=).
Les fichiers connus sont indexes par inode (identifiant filesystem) pour une comparaison instantanee, sans dependre des noms de fichiers.

Phase 2 — Torrents lies (cache)

Si un torrent a deja ete traite et qu'il est dans le cache, on l'identifie directement comme linked.

Phase 3 — Analyse des inodes

Pour chaque fichier de chaque torrent, on determine sa position :

  • Dans les repertoires medias (MEDIA_DIRS)
  • Dans le dossier cross-seed (CROSS_SEED_DIR)
  • Ailleurs (inconnu)

Phase 4 — Classification

Chaque torrent recoit un tag selon la situation de ses fichiers :

Tag Condition
linked Tous les fichiers sont dans les medias, via un inode connu des Arr
cross-linked Tous les fichiers sont dans les medias, mais un au moins est aussi dans CROSS_SEED_DIR
orphan Aucun fichier n'est lie aux medias (hardlink manquant ou torrent orphelin)

Phase 4.5 — pour les torrents orphan, on verifie la duree de seed minimale par tracker. Si elle est atteinte, le tag devient a effacer. Sinon, il reste orphan.

La duree minimale par tracker est demandee une seule fois interactivement, puis stockee dans cleanup/tracker_secrets.conf.

Phase 5 — Reparation (optionnel)

Si AUTO_REPAIR=true, le script tente de creer un hardlink manuel pour chaque fichier orphelin :

  1. Recherche d'un candidat de meme taille dans les medias
  2. Filtre par nom similaire (normalisation des stopwords)
  3. Comparaison par hash (xxhash/md5) pour garantir l'identite binaire
  4. Si match -> rm du fichier orphelin + ln vers le fichier media
  5. Retagage en linked

Phase 6 — Orphelins de disque

Scan des repertoires MEDIA_DIRS pour lister les fichiers qui ne sont :

  • Ni references par Radarr/Sonarr
  • Ni lies a un torrent via hardlink

Rapport genere dans DISK_ORPHAN_LOG.

Utilisation

Lancement manuel

./check_hardlinks.sh

Planification (cron)

Execution toutes les 6 heures :

0 */6 * * * /home/user/qb-dedupe/check_hardlinks.sh >> /home/user/qb-dedupe/lastrun.log 2>&1

Le script est resilient : si vous l'interrompez (Ctrl+C), les caches sont sauvegardes immediatement.

Workflow recommande avec les tags

  1. Lancement initial — tous les torrents se retrouvent tagues.
  2. Filtrage UI — dans qBittorrent, filtrer par orphan.
  3. Reparation — si AUTO_REPAIR=true, les orphelins reparables deviennent linked.
  4. Revue — examiner les torrents restant orphan (non reparables).
  5. Suppression — les torrents a effacer ont depasse la duree de seed requise. Vous pouvez les supprimer en toute securite.

Fichiers generes

Fichier Description
cleanup/hash_cache.txt Cache des hashes de fichiers (accelere la reparation)
cleanup/inode_status.txt Position des inodes (media / cross / inconnu)
cleanup/torrent_status.txt Dernier statut connu de chaque torrent
cleanup/arr_inodes.txt Cache des inodes connus des Arr
cleanup/tracker_secrets.conf Durees minimales de seed par tracker (chmod 600)
cleanup/disk_orphans.log Liste des fichiers medias non references

Depannage

"HTTP 400 (episodefile)" sur Sonarr

Normal, le script bascule automatiquement sur le fallback series -> episodefile?seriesId=. Si le message persiste, verifiez votre cle API et l'URL Sonarr.

Aucun Radarr detecte

Verifiez imperativement la syntaxe des cles composites :

# Mauvais
ARR_CONFIG["VPN"]="radarr|..."
ARR_CONFIG["VPN"]="sonarr|..."  # ecrase la ligne precedente

# Correct
ARR_CONFIG["VPN|radarr"]="url|key"
ARR_CONFIG["VPN|sonarr"]="url|key"

Le cache semble obsole

Supprimez les fichiers dans cleanup/ pour forcer une reinterrogation complete :

rm -f cleanup/*.txt

Les hardlinks ne se creent pas

Le hardlink (naturel ou repare) necessite que les torrents et les medias soient sur le meme volume physique (meme device). Verifiez avec df -h que les chemins partagent le meme point de montage.

Licence

MIT — Utilisation et modification libre. Aucune garantie fournie.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages