diff --git a/videoclub/nas_dl.py b/videoclub/nas_dl.py index 1727b33..bbf9ba7 100644 --- a/videoclub/nas_dl.py +++ b/videoclub/nas_dl.py @@ -1,86 +1,27 @@ """ -nas_dl.py — Anade descargas al NAS Synology via API de Download Station. +nas_dl.py — Carga el modulo compartido vigilancia/nas_dl.py +(anade descargas al NAS Synology via API de Download Station). -Login: POST /webapi/auth.cgi (SYNO.API.Auth, version 6, session DownloadStation) -Crear: POST /webapi/DownloadStation/task.cgi (SYNO.DownloadStation.Task, create) - -La URI puede ser un enlace HTTP directo (p. ej. enlace de RealDebrid), -un magnet o una URL de fichero .torrent. +Las credenciales NAS se leen del .env de vigilancia/ o, como respaldo, +del .env de videoclub/. """ -import json -import os +import importlib.util import sys -import urllib.parse -import urllib.request from pathlib import Path -from dotenv import load_dotenv +BASE = Path(__file__).resolve().parent +VIGILANCIA = BASE.parent / "vigilancia" +REAL = VIGILANCIA / "nas_dl.py" -load_dotenv(Path(__file__).resolve().parent / ".env") +if not REAL.exists(): + raise ImportError("No se encontro vigilancia/nas_dl.py") -NAS_HOST = os.getenv("NAS_HOST", "192.168.50.31").strip().rstrip("/") -NAS_PORT = os.getenv("NAS_PORT", "5000").strip() -NAS_USER = os.getenv("NAS_USER", "").strip() -NAS_PASS = os.getenv("NAS_PASS", "").strip() -NAS_HTTPS = os.getenv("NAS_HTTPS", "0").strip() == "1" +_spec = importlib.util.spec_from_file_location("vigilancia_nas_dl", REAL) +_vig_nas = importlib.util.module_from_spec(_spec) +sys.modules["vigilancia_nas_dl"] = _vig_nas +_spec.loader.exec_module(_vig_nas) -BASE = f"{'https' if NAS_HTTPS else 'http'}://{NAS_HOST}:{NAS_PORT}" +add_torrent = _vig_nas.add_torrent +login = _vig_nas.login - -def _api(path, params, session=None): - data = urllib.parse.urlencode(params).encode() - headers = {"Content-Type": "application/x-www-form-urlencoded"} - if session: - headers["Cookie"] = session - req = urllib.request.Request(f"{BASE}{path}", data=data, headers=headers) - with urllib.request.urlopen(req, timeout=20) as r: - return json.loads(r.read().decode()) - - -def login(): - """Devuelve la cookie de sesion DownloadStation o None.""" - if not NAS_USER or not NAS_PASS: - print("[NAS] Faltan NAS_USER/NAS_PASS en .env") - return None - r = _api("/webapi/auth.cgi", { - "api": "SYNO.API.Auth", - "version": "6", - "method": "login", - "account": NAS_USER, - "passwd": NAS_PASS, - "session": "DownloadStation", - "format": "cookie", - }) - if r.get("success"): - # la cookie viene en Set-Cookie; la reconstruimos con el sid devuelto - sid = r.get("data", {}).get("sid") - return f"sid={sid}" if sid else None - print(f"[NAS] Login fallido: {r.get('error')}") - return None - - -def add_torrent(uri, session=None): - """Anade una URI (http/magnet/.torrent) a Download Station.""" - if not session: - session = login() - if not session: - return False - r = _api("/webapi/DownloadStation/task.cgi", { - "api": "SYNO.DownloadStation.Task", - "version": "3", - "method": "create", - "uri": uri, - }, session=session) - if r.get("success"): - print(f"[NAS] Tarea anadida: {uri[:80]}") - return True - print(f"[NAS] Error al anadir tarea: {r.get('error')}") - return False - - -if __name__ == "__main__": - if len(sys.argv) < 2: - print("Uso: python nas_dl.py ") - sys.exit(1) - ok = add_torrent(sys.argv[1]) - sys.exit(0 if ok else 1) +__all__ = ["add_torrent", "login"] diff --git a/vigilancia/.env.example b/vigilancia/.env.example index 70eac27..0a89319 100644 --- a/vigilancia/.env.example +++ b/vigilancia/.env.example @@ -71,3 +71,10 @@ VOZ_MENSAJE_CONOCIDO={nombres}, ¿qué estás haciendo? No entres ahí. # Monkey de VoiceMonkey que apaga la tele tras el anuncio (p.ej. apagar_tele) ALEXA_APAGAR_TV_MONKEY= + +# NAS Synology Download Station (comando 'descargar' de pi_bot) +NAS_HOST=192.168.50.31 +NAS_PORT=5000 +NAS_USER= +NAS_PASS= +NAS_HTTPS=0 diff --git a/vigilancia/deploy_pi.sh b/vigilancia/deploy_pi.sh new file mode 100644 index 0000000..64a1e05 --- /dev/null +++ b/vigilancia/deploy_pi.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# deploy_pi.sh — Despliegue manual en la Raspberry Pi (minipc). +# Uso en la Pi: bash /home/pi/minipc/vigilancia/deploy_pi.sh +set -e + +REPO=/home/pi/minipc +ENV_FILE=$REPO/vigilancia/.env + +echo "==> git pull en $REPO" +cd "$REPO" +git pull + +echo "==> Comprobar credenciales NAS en $ENV_FILE" +if ! grep -q "^NAS_HOST=" "$ENV_FILE" 2>/dev/null; then + echo "AVISO: falta NAS_HOST/NAS_USER/NAS_PASS en $ENV_FILE" + echo "Anade al final del .env:" + echo " NAS_HOST=192.168.50.31" + echo " NAS_PORT=5000" + echo " NAS_USER=" + echo " NAS_PASS=" + echo " NAS_HTTPS=0" + exit 1 +fi + +echo "==> Reiniciar pi-bot" +sudo systemctl restart pi-bot +sleep 2 +systemctl is-active pi-bot + +echo "==> Comprobar sintaxis nas_dl.py" +cd "$REPO/vigilancia" +python3 -m py_compile nas_dl.py pi_bot.py && echo "OK sintaxis" + +echo "==> Listo. Prueba: envia a @juanjer_casa_bot: descargar " diff --git a/vigilancia/nas_dl.py b/vigilancia/nas_dl.py new file mode 100644 index 0000000..a2abe8b --- /dev/null +++ b/vigilancia/nas_dl.py @@ -0,0 +1,110 @@ +""" +nas_dl.py — Anade descargas al NAS Synology via API de Download Station. +Usado por pi_bot.py (comando 'descargar') y por videoclub.py. + +Login: POST /webapi/auth.cgi (SYNO.API.Auth, version 6, session DownloadStation) +Crear: POST /webapi/DownloadStation/task.cgi (SYNO.DownloadStation.Task, create) + +La URI puede ser un enlace HTTP directo (p. ej. enlace de RealDebrid), +un magnet o una URL de fichero .torrent. +""" +import json +import os +import urllib.parse +import urllib.request + +# Carga .env del directorio del script si existe (no requiere python-dotenv) +BASE = os.path.dirname(os.path.abspath(__file__)) + + +def _load_env(path): + env = {} + try: + for line in open(path): + line = line.strip() + if line and not line.startswith("#") and "=" in line: + k, v = line.split("=", 1) + env[k.strip()] = v.strip() + except Exception: + pass + return env + + +ENV = _load_env(os.path.join(BASE, ".env")) +# Respaldo: si no hay credenciales NAS aqui, probar el .env de videoclub/ +_VIDEOCLUB_ENV = _load_env(os.path.join(BASE, "..", "videoclub", ".env")) +for _k in ("NAS_HOST", "NAS_PORT", "NAS_USER", "NAS_PASS", "NAS_HTTPS"): + if not ENV.get(_k): + ENV[_k] = _VIDEOCLUB_ENV.get(_k, "") +NAS_HOST = os.getenv("NAS_HOST", ENV.get("NAS_HOST", "192.168.50.31")).strip().rstrip("/") +NAS_PORT = os.getenv("NAS_PORT", ENV.get("NAS_PORT", "5000")).strip() +NAS_USER = os.getenv("NAS_USER", ENV.get("NAS_USER", "")).strip() +NAS_PASS = os.getenv("NAS_PASS", ENV.get("NAS_PASS", "")).strip() +NAS_HTTPS = os.getenv("NAS_HTTPS", ENV.get("NAS_HTTPS", "0")).strip() == "1" + +BASE_URL = f"{'https' if NAS_HTTPS else 'http'}://{NAS_HOST}:{NAS_PORT}" + + +def _api(path, params, session=None): + data = urllib.parse.urlencode(params).encode() + headers = {"Content-Type": "application/x-www-form-urlencoded"} + if session: + headers["Cookie"] = session + req = urllib.request.Request(f"{BASE_URL}{path}", data=data, headers=headers) + with urllib.request.urlopen(req, timeout=20) as r: + return json.loads(r.read().decode()) + + +def login(): + """Devuelve la cookie de sesion DownloadStation o None.""" + if not NAS_USER or not NAS_PASS: + print("[NAS] Faltan NAS_USER/NAS_PASS en .env") + return None + try: + r = _api("/webapi/auth.cgi", { + "api": "SYNO.API.Auth", + "version": "6", + "method": "login", + "account": NAS_USER, + "passwd": NAS_PASS, + "session": "DownloadStation", + "format": "cookie", + }) + if r.get("success"): + sid = r.get("data", {}).get("sid") + return f"sid={sid}" if sid else None + print(f"[NAS] Login fallido: {r.get('error')}") + except Exception as e: + print(f"[NAS] Error de login: {e}") + return None + + +def add_torrent(uri, session=None): + """Anade una URI (http/magnet/.torrent) a Download Station.""" + if not session: + session = login() + if not session: + return False + try: + r = _api("/webapi/DownloadStation/task.cgi", { + "api": "SYNO.DownloadStation.Task", + "version": "3", + "method": "create", + "uri": uri, + }, session=session) + if r.get("success"): + print(f"[NAS] Tarea anadida: {uri[:80]}") + return True + print(f"[NAS] Error al anadir tarea: {r.get('error')}") + except Exception as e: + print(f"[NAS] Error: {e}") + return False + + +if __name__ == "__main__": + import sys + if len(sys.argv) < 2: + print("Uso: python nas_dl.py ") + sys.exit(1) + ok = add_torrent(sys.argv[1]) + sys.exit(0 if ok else 1) diff --git a/vigilancia/pi_bot.py b/vigilancia/pi_bot.py index b3a375c..a6f3302 100644 --- a/vigilancia/pi_bot.py +++ b/vigilancia/pi_bot.py @@ -154,6 +154,15 @@ def procesar(texto, chat_id): else: ok = mandar_youtube(url) enviar("🎬 YouTube lanzado en la TV ✅" if ok else "❌ No se pudo lanzar YouTube", chat_id) + elif t.startswith("descargar ") or t.startswith("/descargar "): + uri = texto.split(" ", 1)[1].strip() + if not uri: + enviar("Uso: descargar <enlace|magnet|uri> (anade a Download Station del NAS)", chat_id) + else: + import nas_dl + ok = nas_dl.add_torrent(uri) + enviar("📥 Añadido a Download Station del NAS ✅" if ok + else "❌ No se pudo añadir al NAS", chat_id) elif t in ("ayuda", "/ayuda", "help", "/help"): enviar("Comandos:\n" "estado — estado de la Pi\n" @@ -161,6 +170,7 @@ def procesar(texto, chat_id): "tv <mensaje> — mensaje rápido a la tele\n" "alarma <mensaje> — secuencia completa (sirena x3)\n" "youtube <URL> — reproduce YouTube y apaga al terminar\n" + "descargar <enlace> — añade a Download Station del NAS\n" "panorama — fotos de las cámaras\n" "limpieza — limpia procesos atascados de la Pi\n" "reboot — reinicia la Pi (necesita confirmar con /confirmar-reboot)\n"