[ID] SCRIPT_NAME: synomap_apply_from_plan.sh PROJECT: SEED_MOVER [ROLE] - Moteur unifié d'application des plans synomap : prend un plan produit par daily_qbittorrent_update, le normalise puis applique chaque item de façon strictement séquentielle. - Orchestration complète pour chaque torrent : hardlink (si absent), sidecars (copy/ignore/redownload), bind-mount éventuel, setLocation, recheck contrôlé, resume + tag SYNO. - Regroupe les comportements de v1.2.6 (POSIX simple) et v1.6.9_sequential (séquencement strict + protections API), avec PREVIEW par défaut et WRITE explicite. [CONTEXT] - À exécuter sur OMV avec accès en lecture/écriture à /data/... (source) et /syno/... (destination), les deux montés sur un même device ou compatibles bind. - Toutes les opérations qBittorrent sont faites via API locale, nécessitant accès réseau (typiquement localhost:8080). - Dépendances requises : /bin/sh, curl, jq, sed, awk, tr, nl, ln, mkdir, stat, cp, mount, umount, mountpoint, sleep. - Charge un fichier SYNO_ENV si présent ; sinon lit synomap.env dans le dossier du script (QB_URL/QB_USER/QB_PASS obligatoires). [INPUTS] - CLI : PLANFILE : fichier plan (format daily_qbittorrent_update). --write : exécute réellement (sinon PREVIEW total). --sleep-seconds N : délai entre torrents (défaut 2). --move-timeout-sec N : délai maximal pour enter_checking (défaut 900). --recheck-timeout-sec N : délai maximal pour wait_check_done (défaut 900). --sidecars MODE : copy|ignore|redownload (défaut copy). --no-bind : désactive mount --bind. --debug : journalisation détaillée + dumps API. - Environnement : QB_URL/QB_USER/QB_PASS obligatoires (erreur immédiate si manquants). SYNO_ENV optionnel ; UMASK, TMPDIR… peuvent être exportés mais les options CLI priment. - Format du plan : Hash SHA1 (40 hex) + lignes "plan: ln 'SRC' -> 'DST'". Normalisation automatique : hash|src|dst dans ITEMS. infer_hash : si hash absent → résolution dépendante de catégorie/noms/contenu côté qBittorrent. [OUTPUTS] - PREVIEW : - liste les actions qui seraient menées : pause, hardlink, stratégie sidecars, bind, setLocation, recheck, resume/tag. - indique pour chaque torrent l’état du lien (existe / créé) et la résolution de hash. - WRITE : - mkdir -p DEST_DIR, ln pour les hardlinks manquants. - sidecars selon la stratégie (copy + filePrio / ignore / redownload). - pause torrent, AutoTMM off, bind éventuel du save_path d’origine, setLocation, recheck contrôlé puis resume + tag SYNO. - nettoyage des mounts (umount) et suppression des fichiers temporaires via trap. - Aucune log persistante : uniquement stdout/stderr. [FAILURE_MODES] - PLANFILE absent → die "plan file required" (exit ≠ 0). - PLANFILE illisible → die "plan not found". - Env QB_URL/QB_USER/QB_PASS manquants → die. - Dépendance système manquante (mount, jq…) → die. - Login qBittorrent échoué → die "qB login failed". - Parsing plan : entrée ignorée si SRC/DST manquant ; hash non déductible → WARN + skip. - WRITE : ln/mkdir/bind/stat → WARN ; suite non bloquée (best effort). - Recheck timeout → WARN ; torrent traité mais intégrité incertaine. - Crash après bind → risque de bind laissé actif (trap tente cleanup mais non garanti si kill -9). [OBSERVATIONS & LIMITES] - Dépend fortement de jq, curl et mount --bind (root requis). - Les timeouts sont fixes (MOVE/RECHECK) → ralentissements possibles sur NAS saturé. - Aucun mécanisme de log persistent ; diagnostic via stdout seulement. - La résolution du hash (infer_hash) réduit drastiquement les mauvaises correspondances, mais dépend toujours de la cohérence entre plan et qBittorrent. [HISTORIQUE] - Script unifiant la lignée apply_from_plan v1.2.x (POSIX simple) et v1.6.x (séquentiel strict). - Préserve la compatibilité avec les plans de daily_qbittorrent_update_v1.32c. - Introduit des garde-fous JSON (protection jq), un parsing plan robuste et un nettoyage automatique des temporaires. [RECENT_CHANGES] - 2025-11-15 : - Réécriture complète de la résolution de hash pour supprimer les mauvaises correspondances (ex : "Scream" mappé sur "Mickey"). - Ajout d’un resolver déterministe basé sur la catégorie (sonarr/radarr), puis le nom, puis fallback contrôlés. - Mise en cache par run pour accélérer les résolutions multiples. - Intégration directe du resolver dans la boucle principale.