169 lines
6.0 KiB
Python
169 lines
6.0 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
"""
|
||
|
|
SABnzbd Post-Processing Script — UNFR Mark HS
|
||
|
|
=====================================================
|
||
|
|
En cas d'échec de vérification/réparation (statut 1),
|
||
|
|
parse le NZB original (gzippé) pour extraire le randomid
|
||
|
|
depuis <meta type="tag"> et appelle l'API UNFR markhs.
|
||
|
|
|
||
|
|
Installation :
|
||
|
|
1. Éditez la variable API_KEY ci-dessous avec votre clé UNFR.
|
||
|
|
2. Copiez ce script dans le dossier "scripts" de SABnzbd.
|
||
|
|
3. Rendez le script exécutable (chmod +x sabnzbd_markhs.py) si vous êtes sous Linux/macOS.
|
||
|
|
4. Dans SABnzbd → Config → Dossiers → Dossier des scripts, assurez-vous que le dossier est correct.
|
||
|
|
5. Assignez ce script à la catégorie voulue (ou en post-processing par défaut).
|
||
|
|
|
||
|
|
Variables d'environnement SABnzbd utilisées :
|
||
|
|
SAB_ORIG_NZB_GZ = Chemin vers le NZB original compressé (.nzb.gz)
|
||
|
|
|
||
|
|
Arguments positionnels SABnzbd :
|
||
|
|
7 = Statut post-processing (1 = échec vérification/réparation)
|
||
|
|
"""
|
||
|
|
|
||
|
|
import sys
|
||
|
|
import os
|
||
|
|
import gzip
|
||
|
|
import xml.etree.ElementTree as ET
|
||
|
|
import urllib.request
|
||
|
|
import urllib.parse
|
||
|
|
import urllib.error
|
||
|
|
|
||
|
|
# ──────────────────────────────────────────────
|
||
|
|
# CONFIGURATION UNFR
|
||
|
|
# ──────────────────────────────────────────────
|
||
|
|
# Remplacez "METTRE_VOTRE_CLE_ICI" par votre clé API UNFR
|
||
|
|
API_KEY = "METTRE_VOTRE_CLE_ICI"
|
||
|
|
|
||
|
|
API_BASE_URL = "https://unfr.pw/api_verif_hs.php"
|
||
|
|
# ──────────────────────────────────────────────
|
||
|
|
|
||
|
|
def log_info(msg):
|
||
|
|
"""Affiche un message d'information visible dans les logs SABnzbd."""
|
||
|
|
print(f"[INFO] {msg}", flush=True)
|
||
|
|
|
||
|
|
def log_error(msg):
|
||
|
|
"""Affiche un message d'erreur visible dans les logs SABnzbd."""
|
||
|
|
print(f"[ERROR] {msg}", flush=True)
|
||
|
|
|
||
|
|
def extract_randomid(nzb_gz_path):
|
||
|
|
"""
|
||
|
|
Décompresse le NZB gzippé, parse le XML et retourne
|
||
|
|
la valeur de <meta type="tag">...</meta>.
|
||
|
|
"""
|
||
|
|
log_info(f"Lecture NZB : {nzb_gz_path}")
|
||
|
|
|
||
|
|
with gzip.open(nzb_gz_path, "rb") as f:
|
||
|
|
xml_data = f.read()
|
||
|
|
|
||
|
|
root = ET.fromstring(xml_data)
|
||
|
|
|
||
|
|
# Le namespace NZB standard
|
||
|
|
ns = {"nzb": "http://www.newzbin.com/DTD/2003/nzb"}
|
||
|
|
|
||
|
|
# Cherche <meta type="tag">value</meta> avec namespace
|
||
|
|
for meta in root.findall(".//nzb:head/nzb:meta", ns):
|
||
|
|
if meta.get("type") == "tag" and meta.text:
|
||
|
|
return meta.text.strip()
|
||
|
|
|
||
|
|
# Fallback sans namespace (certains NZB n'en ont pas)
|
||
|
|
for meta in root.findall(".//head/meta"):
|
||
|
|
if meta.get("type") == "tag" and meta.text:
|
||
|
|
return meta.text.strip()
|
||
|
|
|
||
|
|
# Dernier fallback : chercher partout
|
||
|
|
for meta in root.iter():
|
||
|
|
tag_local = meta.tag.split("}")[-1] if "}" in meta.tag else meta.tag
|
||
|
|
if tag_local == "meta" and meta.get("type") == "tag" and meta.text:
|
||
|
|
return meta.text.strip()
|
||
|
|
|
||
|
|
return None
|
||
|
|
|
||
|
|
def call_markhs(randomid, extra_info=""):
|
||
|
|
"""Appelle l'API UNFR pour marquer la release HS."""
|
||
|
|
params = {
|
||
|
|
"apikey": API_KEY,
|
||
|
|
"action": "markhs",
|
||
|
|
"randomid": randomid,
|
||
|
|
}
|
||
|
|
if extra_info:
|
||
|
|
params["extra_info"] = extra_info
|
||
|
|
|
||
|
|
query = urllib.parse.urlencode(params, quote_via=urllib.parse.quote)
|
||
|
|
url = f"{API_BASE_URL}?{query}"
|
||
|
|
|
||
|
|
# Masquage de la clé API et décodage pour un affichage propre dans les logs
|
||
|
|
log_url = url.replace(API_KEY, "***")
|
||
|
|
log_info(f"Appel API : {urllib.parse.unquote(log_url)}")
|
||
|
|
|
||
|
|
try:
|
||
|
|
req = urllib.request.Request(url, method="GET")
|
||
|
|
with urllib.request.urlopen(req, timeout=30) as resp:
|
||
|
|
body = resp.read().decode("utf-8", errors="replace")
|
||
|
|
log_info(f"Réponse API ({resp.status}) : {body[:500]}")
|
||
|
|
return resp.status == 200
|
||
|
|
except urllib.error.HTTPError as e:
|
||
|
|
body = e.read().decode("utf-8", errors="replace")
|
||
|
|
log_error(f"Erreur HTTP {e.code} : {e.reason} | body={body[:500]}")
|
||
|
|
except urllib.error.URLError as e:
|
||
|
|
log_error(f"Erreur réseau : {e.reason}")
|
||
|
|
except Exception as e:
|
||
|
|
log_error(f"Erreur inattendue : {e}")
|
||
|
|
|
||
|
|
return False
|
||
|
|
|
||
|
|
def main():
|
||
|
|
log_info(f"Script démarré - python {sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}")
|
||
|
|
|
||
|
|
if len(sys.argv) < 8:
|
||
|
|
log_error("Pas assez d'arguments — ce script doit être lancé par SABnzbd lors du post-processing.")
|
||
|
|
sys.exit(0)
|
||
|
|
|
||
|
|
clean_name = sys.argv[3]
|
||
|
|
category = sys.argv[5]
|
||
|
|
status = sys.argv[7]
|
||
|
|
|
||
|
|
log_info(f"Job : {clean_name}")
|
||
|
|
log_info(f"Catégorie : {category}")
|
||
|
|
log_info(f"Statut : {status}")
|
||
|
|
|
||
|
|
# ── Seul le statut 1 (échec vérification/réparation) nous intéresse ──
|
||
|
|
if status != "1":
|
||
|
|
log_info("Statut non concerné (téléchargement réussi) — rien à signaler.")
|
||
|
|
sys.exit(0)
|
||
|
|
|
||
|
|
if API_KEY == "METTRE_VOTRE_CLE_ICI" or not API_KEY:
|
||
|
|
log_error("L'API Key n'a pas été configurée dans le script. Veuillez éditer sabnzbd_markhs.py.")
|
||
|
|
sys.exit(0)
|
||
|
|
|
||
|
|
# ── Récupérer le chemin du NZB gzippé ──
|
||
|
|
nzb_gz_path = os.environ.get("SAB_ORIG_NZB_GZ", "")
|
||
|
|
if not nzb_gz_path or not os.path.isfile(nzb_gz_path):
|
||
|
|
log_error(f"SAB_ORIG_NZB_GZ introuvable ou invalide ({nzb_gz_path})")
|
||
|
|
sys.exit(0)
|
||
|
|
|
||
|
|
# ── Extraire le randomid depuis <meta type="tag"> ──
|
||
|
|
try:
|
||
|
|
randomid = extract_randomid(nzb_gz_path)
|
||
|
|
except Exception as e:
|
||
|
|
log_error(f"Erreur lors du parsing du fichier NZB : {e}")
|
||
|
|
sys.exit(0)
|
||
|
|
|
||
|
|
if not randomid:
|
||
|
|
log_info("Aucune balise <meta type=\"tag\"> trouvée dans le NZB. Abandon.")
|
||
|
|
sys.exit(0)
|
||
|
|
|
||
|
|
log_info(f"Random ID : {randomid}")
|
||
|
|
|
||
|
|
# ── Appel API ──
|
||
|
|
extra_info = f"SABnzbd: Echec verification / reparation | job={clean_name}"
|
||
|
|
success = call_markhs(randomid, extra_info)
|
||
|
|
|
||
|
|
if success:
|
||
|
|
log_info("Release marquée HS avec succès sur UNFR.")
|
||
|
|
else:
|
||
|
|
log_error("Impossible de marquer la release HS sur UNFR.")
|
||
|
|
|
||
|
|
sys.exit(0)
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
main()
|