Vue normale

Reçu aujourd’hui — 26 janvier 2026

Decypharr & AllDebrid : No debrid clients available or no slots found + script de nettoyage de magnets par Bouby

Par :Aerya
26 janvier 2026 à 14:07

Les versions :latest et :fenrir-xx embarquent une fonctionnalité qui interroge les débrideurs pour connaître le nombre de slots d’upload disponibles sur le compte utilisé.
Problème : AllDebrid ne gère pas ça via son API.
Second effet Kiss Cool : cette option ne peut pour l’instant pas être désactivée, donc AD en erreur d’upload quand un magnet/.torrent n’est pas en cache.

L’astuce consiste à revenir sur une version plus ancienne de Decypharr, la :beta fonctionnant.

services:
  decypharr:
    image: ghcr.io/sirrobot01/decypharr:beta
    #image: cy01/blackhole:fenrir
    #image: cy01/blackhole:fenrir-09
    container_name: decypharr
    restart: always
    privileged: true
    cap_add:
      - SYS_ADMIN
    security_opt:
      - apparmor:unconfined
    ports:
      - 8282:8282
    volumes:
      - /mnt/:/mnt/:rshared
      - /mnt/Fichiers/decypharr/qbit:/mnt/Fichiers/decypharr/qbit
      - /mnt/Docker/decypharr/:/app
    environment:
      - TZ=Europe/Paris
    devices:
      - /dev/fuse:/dev/fuse:rwm

Et j’en profite pour vous présenter l’excellent script de Bouby, lecteur du blog (\o/), qui permet de nettoyer les magnets liés à un compte AD. En effet, chez AD un compte est limité à 5000 liens. Il faut donc parfois faire un peu de ménage, surtout quand on fait des bibliothèques Plex/Jellyfin où il faut clairement plusieurs comptes AD.
Vous les voyez sur l’onglet Magnets de votre compte ou via l’API

adscript
status	"success"
data	{ magnets: (704)[…] }


Un lien est ajouté quand on envoie un magnet ou .torrent sur son compte AD (captain Obvious!) mais aussi à CHAQUE recherche de contenu dessus via les outils Decypharr, DMM, Vortex, les addons sources de Stremio etc. Même si un contenu est en cache, le fait de le chercher en ajoutant un magnet/.torrent crée un lien sur votre compte.
Je n’ai pas cherché et ne sais absolument pas comment AD gère ça côté utilisateurs, si des liens vieux de plus de X mois/années/heures sont retirés ou non.

Quoi qu’il en soit voici le script Python de Bouby (encore merci !). J’ai augmenté les seuils en fonction de la limite de 5000 liens/compte.

#!/usr/bin/env python3
import requests
from datetime import datetime, timedelta

# ----------------------------------------
# CONFIGURATION DU SCRIPT (variables en dur)
# ----------------------------------------

# Clé API AllDebrid — obligatoire pour accéder aux magnets
API_KEY = "xxx"

# Dry-run : True = le script affiche ce qu’il ferait sans supprimer
# False = suppression réelle
DRY_RUN = False

# -----------------------------
# Seuils et limites pour la purge
# -----------------------------
THRESHOLD_HIGH = 4700  # déclenche la purge normale si nombre total de magnets > 900
THRESHOLD_LOW = 2700   # limite minimale après purge pour ne pas trop supprimer =700
MAX_DELETE = 2000      # nombre maximum de magnets à supprimer en mode normal
MIN_AGE_DAYS = 7      # ne supprimer que les magnets plus vieux que X jours (sauf urgence)

# -----------------------------
# Mode urgence
# -----------------------------
EMERGENCY_THRESHOLD = 4900  # si le nombre total de magnets > 950, on active la purge d'urgence
EMERGENCY_DELETE = 2000     # nombre de magnets à supprimer immédiatement en urgence, ignore l'âge minimum

# -----------------------------
# API endpoint AllDebrid
# -----------------------------
# v4.1 est le nouvel endpoint pour récupérer les magnets
BASE_URL = "https://api.alldebrid.com/v4.1/magnet/status"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}

# -----------------------------
# Catégories de statut
# -----------------------------
# Ces statuts sont utilisés pour trier les magnets par priorité
FAILED = {"Error", "error"}        # magnets ayant rencontré une erreur
IN_PROGRESS = {"Processing"}       # magnets en cours de téléchargement
DONE = {"Finished", "Ready"}       # magnets terminés

# ----------------------------------------
# Récupérer tous les magnets depuis l’API
# ----------------------------------------
def get_magnets():
    """
    Récupère la liste des magnets depuis l’API AllDebrid.
    Retourne une liste vide si erreur ou endpoint obsolète.
    """
    try:
        r = requests.get(BASE_URL, headers=HEADERS, timeout=30)
        r.raise_for_status()
        data = r.json()

        # Vérifie que la réponse contient bien les magnets
        if "data" not in data or "magnets" not in data["data"]:
            print("❌ Réponse API inattendue :")
            print(data)
            return []

        return data["data"]["magnets"]

    except requests.RequestException as e:
        print("❌ Erreur réseau ou API :", e)
        return []

# ----------------------------------------
# Supprimer un magnet par son ID
# ----------------------------------------
def delete_magnet(magnet_id):
    """
    Supprime un magnet via l’API.
    Si DRY_RUN = True, n’effectue pas la suppression mais affiche l’action.
    """
    delete_url = "https://api.alldebrid.com/v4/magnet/delete"
    if DRY_RUN:
        print(f"[DRY-RUN] DELETE {magnet_id}")
        return

    try:
        # POST pour supprimer le magnet
        r = requests.post(delete_url, headers=HEADERS, data={"id": magnet_id})
        r.raise_for_status()
        print(f"✅ Supprimé magnet {magnet_id}")
    except Exception as e:
        print(f"❌ Erreur suppression {magnet_id} :", e)

# ----------------------------------------
# Trier et filtrer les magnets
# ----------------------------------------
def bucketize(magnets, ignore_age=False):
    """
    Trie les magnets par statut et filtre par âge.
    ignore_age=True ignore la limite MIN_AGE_DAYS (mode urgence)
    """
    # Date limite selon l'âge minimum
    cutoff = datetime.utcnow() - timedelta(days=MIN_AGE_DAYS)

    # Séparer les magnets par statut pour appliquer la priorité
    failed, progress, done = [], [], []

    for m in magnets:
        status = m.get("status", "")
        created = m.get("uploadDate") or m.get("completionDate") or 0
        created_dt = datetime.utcfromtimestamp(created) if created else datetime.utcnow()

        # Filtrer par âge sauf si mode urgence
        if not ignore_age and created_dt > cutoff:
            continue

        # Trier par statut
        if status in FAILED:
            failed.append(m)
        elif status in IN_PROGRESS:
            progress.append(m)
        elif status in DONE:
            done.append(m)

    # Trier chaque catégorie par date de création (anciens d’abord)
    for lst in (failed, progress, done):
        lst.sort(key=lambda x: x.get("uploadDate", 0))

    # Retourner tous les magnets dans l'ordre priorité : failed → progress → done
    return failed + progress + done

# ----------------------------------------
# Fonction principale
# ----------------------------------------
def main():
    """
    Logique principale :
    - Récupère tous les magnets
    - Vérifie les seuils
    - Applique le mode urgence si nécessaire
    - Supprime les magnets selon les règles
    """
    magnets = get_magnets()
    total = len(magnets)
    print(f"Total magnets : {total}, Dry-run : {DRY_RUN}")

    # Mode urgence
    if total > EMERGENCY_THRESHOLD:
        print("🚨 MODE URGENCE activé")
        ordered = bucketize(magnets, ignore_age=True)
        to_delete = ordered[:EMERGENCY_DELETE]

    # Mode normal
    elif total > THRESHOLD_HIGH:
        print("Mode normal")
        ordered = bucketize(magnets, ignore_age=False)
        max_deletions = min(MAX_DELETE, total - THRESHOLD_LOW)
        to_delete = ordered[:max_deletions]

    else:
        print("Seuil non atteint → rien à faire.")
        return

    # Supprimer les magnets sélectionnés
    for m in to_delete:
        mid = m.get("id")
        name = m.get("filename") or m.get("name") or ""
        status = m.get("status")
        print(f"→ Suppression {status} : {name} (id={mid})")
        delete_magnet(mid)

    print("✨ Purge terminée.")

# ----------------------------------------
# Exécution du script
# ----------------------------------------
if __name__ == "__main__":
    main()

Comme il le suggère, je le fais tourner avec un Docker Alpine. Chaque nuit à 3h.

services:
  alldebrid-purger:
    image: python:3.12-alpine
    container_name: alldebrid-purger
    restart: always
    working_dir: /app
    command: >
      sh -c "
      apk add --no-cache py3-pip &&
      pip install --no-cache-dir requests &&
      echo '0 3 * * * python /app/purge_alldebrid_magnets.py >> /logs/purge.log 2>&1'
      > /etc/crontabs/root &&
      crond -f -d 8
      "
    volumes:
      - /mnt/Docker/alldebrid_purger:/app:ro
      - /mnt/Docker/alldebrid_purger/logs:/logs

Loading

Reçu — 25 août 2025

Streaming à la carte sans stockage local : Decypharr, débrideurs et torrenting

Par :Aerya
25 août 2025 à 17:53

Je continue mon tour d’horizon des solutions de streaming à la carte, légales ou non selon les pays, et qui permet de se créer et maintenir une bibliothèque multimédia sans stockage local.

Je n’aborderai pas publiquement les solutions de streaming depuis des .nzb directement.

Sur le même principe que Stremio, Vortex, ou RDT-Client/Zurg, ou encore DebridMediaManager, on peut utiliser Decypharr pour simuler un client BitTorrent (qBittorrent) et streaming le contenu de .torrent depuis des débrideurs, dont TorBox.

Je vais détailler ici l’installation manuelle de Decypharr, Prowlarr et Radarr/Sonarr/Plex mais tout est notamment inclus dans le script SSDv2 (avec une grosse et belle nouveauté à venir).

J’en profite pour remercier Laster13 et Teal-C pour leurs réponses à mes questions.

La dernière mouture de Decypharr embarque rClone et sa configuration sera automatisée. Mes tests sont réalisés avec Prowlarr et des indexeurs FR publics dont YGG via « api.eu », le débrideur AllDebrid ; et je ne « tape » que dans le contenu en cache chez AD (donc aucun téléchargement).
Verdict : RàS pour les films, quelques saisons de séries manquantes uniquement dans le cache, OK avec le téléchargement.

Le plus important est de bien comprendre que si on demande aux *arrs d’organiser les bibliothèques dans /mnt/Bibliothèques, ils ne vont y mettre que des symlinks vers le montage rClone d’AllDebrid (/mnt/decypharr).
Les *arrs, pour traiter les fichiers, tout comme Plex (ou autre) pour leur lecture, doivent avoir accès à ce montage rClone.
C’est pourquoi il est impératif de monter ce dossier en volume pour chaque Docker devant y avoir accès.

Arborescence du test :

aerya@StreamBox:/$ tree -L 2 /mnt/
/mnt/
├── Bibliothèques
│   ├── Films
│   └── Séries
├── Data
│   ├── lost+found
│   ├── quotaless
│   └── rCloneCache
└── decypharr
    ├── alldebrid
    └── qbit
        ├── radarr
        └── sonarr

Bibliothèques : dossier racine pour les *arrs
Data/rCloneCache : j’avais déjà le dossier pour autre chose, j’utilise le même

decypharr/alldebrid : montage de mon compte AD
decypharr/qbit : dossier de téléchargement (simulé) des fichiers. Les sous-dossiers radarr/sonarr seront ajoutés automatiquement lors de la configuration de Decypharr (tags)

Il faut installer et lancer Decypharr avant le reste vu que tout dépend ensuite du montage du débrideur et des symlinks. Idem s’il faut relancer le Docker Decypharr, il faut relancer les autres ensuite.

services:
  decypharr:
    image: cy01/blackhole:latest
    container_name: decypharr
    restart: always
    cap_add:
      - SYS_ADMIN
    security_opt:
      - apparmor:unconfined
    ports:
      - 8282:8282
    volumes:
      - /mnt:/mnt:rshared
      - /mnt/decypharr/qbit:/mnt/decypharr/qbit
      - /home/aerya/docker/decypharr/configs/:/app
    environment:
      - TZ=Europe/Paris
      - PUID=0
      - PGID=0
    devices:
      - /dev/fuse:/dev/fuse:rwm
    labels:
      - com.centurylinklabs.watchtower.enable=true

/mnt/decypharr/qbit devant être commun aux *arrs et Plex, on y montera /mnt:/mnt dans chaque ainsi que /mnt/decypharr/qbit:/mnt/decypharr/qbit dans les *arrs qui sauront directement où aller chercher les fichiers à traiter.

La configuration n’est pas vraiment expliquée dans la doc, la mienne n’est peut-être pas optimale mais fonctionne.

Je n’ai rien modifié dans l’onglet général. J’ai testé les notifications Discord (attention on voit la passkey des trackers utilisés) mais y’a vraiment pas d’intérêt si on ne fait qu’utilise le cache du débrideur (pas de téléchargement).

decypharr1

Pour la partie débrideur, on fait notre choix, on colle une clé API (ou plusieurs) et j’ai configuré le montage dans

/mnt/decypharr/alldebrid/__all__


On peut ajouter plusieurs débrideurs. On pourra les attribuer à un *arr lié à Decypharr et même les sélectionner lors de l’ajout manuel d’un .torrent

J’utilise le WebDav et je NE COCHE PAS la case Download Uncached pour qu’il ne télécharge aucun fichier (sur AD) mais n’utilise que son cache.
Je fais ça parce que je voulais tester le cache d’AD et ne voulais pas télécharger des fichiers sans les partager. On peut tout à fait faire les 2 mais attention, il n’y aura pas de seed (donc ratio 0). A ne pas faire chez les Tier 1 sous peine de voir son compte banni !
Certains ont créé des scripts qui captent les .torrent utilisés pour les mettre en seed depuis un client local ou sur un serveur. Dans ce cas, Decypharr est utilisable sans crainte sur les trackers privés.

decypharr2

Configuration du client qBittorrent émulé. Je met le chemin qui correspond au volume local dont je parlais au début

/mnt/decypharr/qbit
decypharr3

On peut ajouter les *arrs soit depuis Decypharr soit depuis les *arrs eux-mêmes. Je n’ai qu’AD en débrideur j’ai donc laissé la sélection auto mais on peut choisir.
Par exemple Radarr sur AD et Sonarr sur RD ou Sonarr4K sur TB etc. Selon les goût de chacun. Ce qui est certain, c’est que le cache de contenu MULTi/FRENCH est évidemment plus important sur AD et RD que sur TB, principalement utilisé par les anglophones pour Stremio.

decypharr4

Le Repair est le contrôle et la réparation des symlinks. Qui peuvent être cassés si le contenu lié est effacé du débrideur. Dans ce cas, selon la config, ça peut soit rechercher sur le débrideur (si le fichier a été renommé, vu que ça fonctionne avec le hash et non le nom) soit ça ira chercher un autre .torrent via les *arrs.
Pour l’instant je laisse l’option par défaut à savoir « per torrent » mais il est peut-être préférable d’utiliser « per file ». Dans le cas d’un .torrent de saison complètement, que ça ne recherche que l’épisode manquant (du cache) plutôt que de tout relancer.
Je l’ai mis en autotmatique, toutes les 24h.

decypharr5


Et enfin la partie rClone. Le monte globalement dans /mnt/decypharr et souhaite utiliser un cache (100Go) dans un dossier (et disque) qui me sert déjà à ça : /mnt/Data/rCloneCache
On peut sans doute améliorer cette configuration mais Plex lit un fichier de 94Go sans broncher…


decypharr6

Avec le mode de téléchargement activé, voici le rendu. PausedUP signifie que le .torrent est complété (et de fait plus en seed).

decypharr7

Je ne montre que l’exemple de configuration de Radarr. Pour Sonarr c’est la même logique. Chez moi c’est derrière un VPN pour des interrogations de sources hors Prowlarr et NZBHydra, il n’y a donc pas de port de publié.
Je retire tous les volumes par défaut pour ne monter que /mnt/decypharr/qbit:/mnt/decypharr/qbit et /mnt:/mnt

services:
  radarr:
    container_name: radarr
    restart: always
    network_mode: container:gluetun-mullvad
    environment:
      - TZ=Europe/Paris
      - PUID=0
      - PGID=0
    volumes:
      - /home/aerya/docker/radarr:/config
      - /mnt/decypharr/qbit:/mnt/decypharr/qbit
      - /mnt:/mnt
    labels:
      - com.centurylinklabs.watchtower.enable=true
    image: linuxserver/radarr:nightly

Configuration du dossier racine : /mnt/Bibliothèques/Films

radarr1

Configuration du client de téléchargement si on n’a pas ajouté Radarr depuis Decypharr. Il faut sélectionner qBittorrent

radarr2

Et dans l’adresse il faut mettre l’URL (ou l’IP) de Decypharr et le port (et SSL si vous utilisez une URL et un reverse proxy).
Le nom d’utilisateur et le mot de passe sont l’URL de Radarr et sa clé API.
La catégorie : radarr (et donc sonarr pour Sonarr ofc!)

Ne surtout pas cocher la case Sequential Order (sinon ça DL localement)

radarr3

Et comme le même chemin/volume est monté dans chaque Docker, il n’y a aucun Remote Path Mapping à mettre

radarr4

Lors de l’ajout d’un film, la complétion est quasi instantanée pour peu que le fichier cherché soit déjà dans le cache d’AllDebrid. Si vous activez le téléchargement des fichiers non encore en cache, ça peut prendre quelques minutes.


radarr5

En regardant de plus près, on voit bien que c’est un symlink vers le cache d’AllDebrid

aerya@StreamBox:/mnt/Bibliothèques/Films$ ls -l Alien\ -\ Romulus\ \(2024\)/
total 4
lrwxrwxrwx 1 root root 208 Aug 25 18:55 Alien.Romulus.2024.MULTi.VFF.2160p.UHD.BluRay.REMUX.DV.HDR.HEVC.TrueHD.7.1.Atmos-ONLY.mkv -> /mnt/decypharr/alldebrid/__all__/Alien.Romulus.2024.MULTi.VFF.2160p.UHD.BluRay.REMUX.DV.HDR.HEVC.TrueHD.7.1.Atmos-ONLY/Alien.Romulus.2024.MULTi.VFF.2160p.UHD.BluRay.REMUX.DV.HDR.HEVC.TrueHD.7.1.Atmos-ONLY.mkv
ncdu 1.19 ~ Use the arrow keys to navigate, press ? for help
--- /mnt/Bibliothèques/Films/Alien - Romulus (2024)- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@   4.0 KiB [###################################################]  Alien.Romulus.2024.MULTi.VFF.2160p.UHD.BluRay.REMUX.DV.HDR.HEVC.TrueHD.7.1.Atmos-ONLY.mkv


Plus globalement, tous les fichiers sont bien chez AllDebrid, monté via WebDav/rClone et je n’ai localement que des symlinks qui ne prennent aucune place.
En théorie, on pourrait se faire cette installation sur une Carte MicroSD de quelques Go 🙂

ncdu 1.19 ~ Use the arrow keys to navigate, press ? for help
--- /mnt ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
   15.9 TiB [###################################################] /decypharr
    4.0 MiB [                                                   ] /Bibliothèques

Pour terminer, la configuration de Plex. Toujours avec /mnt:/mnt de monté pour garantir l’accès aux symlinks.

services:
  pms-docker:
    container_name: plex
    restart: always
    ports:
      - 32400:32400/tcp
      - 33401:33401/tcp
      - 3005:3005/tcp
      - 8324:8324/tcp
      - 32469:32469/tcp
      - 1900:1900/udp
      - 32410:32410/udp
      - 32412:32412/udp
      - 32413:32413/udp
      - 32414:32414/udp
    environment:
      - PLEX_UID=0
      - PLEX_GID=0
      - TZ=Europe/Paris
      - PLEX_CLAIM=claim-xxx
      - ADVERTISE_IP=http://192.168.0.163:32400/
    hostname: plex.xxx.xxx
    volumes:
      - /home/aerya/docker/plex:/config
      - /mnt:/mnt
      - type: tmpfs
        target: /transcode
        tmpfs:
          size: 4g
    labels:
      - com.centurylinklabs.watchtower.enable=true
    image: plexinc/pms-docker:latest
plex1
plex2

Loading

❌