03-ago: [videoclub] descarga a NAS Synology via Download Station + comando 'descargar' en pi_bot (deploy en Pi)
This commit is contained in:
@@ -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)
|
Las credenciales NAS se leen del .env de vigilancia/ o, como respaldo,
|
||||||
Crear: POST /webapi/DownloadStation/task.cgi (SYNO.DownloadStation.Task, create)
|
del .env de videoclub/.
|
||||||
|
|
||||||
La URI puede ser un enlace HTTP directo (p. ej. enlace de RealDebrid),
|
|
||||||
un magnet o una URL de fichero .torrent.
|
|
||||||
"""
|
"""
|
||||||
import json
|
import importlib.util
|
||||||
import os
|
|
||||||
import sys
|
import sys
|
||||||
import urllib.parse
|
|
||||||
import urllib.request
|
|
||||||
from pathlib import Path
|
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("/")
|
_spec = importlib.util.spec_from_file_location("vigilancia_nas_dl", REAL)
|
||||||
NAS_PORT = os.getenv("NAS_PORT", "5000").strip()
|
_vig_nas = importlib.util.module_from_spec(_spec)
|
||||||
NAS_USER = os.getenv("NAS_USER", "").strip()
|
sys.modules["vigilancia_nas_dl"] = _vig_nas
|
||||||
NAS_PASS = os.getenv("NAS_PASS", "").strip()
|
_spec.loader.exec_module(_vig_nas)
|
||||||
NAS_HTTPS = os.getenv("NAS_HTTPS", "0").strip() == "1"
|
|
||||||
|
|
||||||
BASE = f"{'https' if NAS_HTTPS else 'http'}://{NAS_HOST}:{NAS_PORT}"
|
add_torrent = _vig_nas.add_torrent
|
||||||
|
login = _vig_nas.login
|
||||||
|
|
||||||
|
__all__ = ["add_torrent", "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 <uri|magnet|enlace>")
|
|
||||||
sys.exit(1)
|
|
||||||
ok = add_torrent(sys.argv[1])
|
|
||||||
sys.exit(0 if ok else 1)
|
|
||||||
|
|||||||
@@ -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)
|
# Monkey de VoiceMonkey que apaga la tele tras el anuncio (p.ej. apagar_tele)
|
||||||
ALEXA_APAGAR_TV_MONKEY=
|
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
|
||||||
|
|||||||
34
vigilancia/deploy_pi.sh
Normal file
34
vigilancia/deploy_pi.sh
Normal file
@@ -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=<usuario>"
|
||||||
|
echo " NAS_PASS=<password>"
|
||||||
|
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 <enlace|magnet>"
|
||||||
110
vigilancia/nas_dl.py
Normal file
110
vigilancia/nas_dl.py
Normal file
@@ -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 <uri|magnet|enlace>")
|
||||||
|
sys.exit(1)
|
||||||
|
ok = add_torrent(sys.argv[1])
|
||||||
|
sys.exit(0 if ok else 1)
|
||||||
@@ -154,6 +154,15 @@ def procesar(texto, chat_id):
|
|||||||
else:
|
else:
|
||||||
ok = mandar_youtube(url)
|
ok = mandar_youtube(url)
|
||||||
enviar("🎬 YouTube lanzado en la TV ✅" if ok else "❌ No se pudo lanzar YouTube", chat_id)
|
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: <b>descargar <enlace|magnet|uri></b> (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"):
|
elif t in ("ayuda", "/ayuda", "help", "/help"):
|
||||||
enviar("Comandos:\n"
|
enviar("Comandos:\n"
|
||||||
"<b>estado</b> — estado de la Pi\n"
|
"<b>estado</b> — estado de la Pi\n"
|
||||||
@@ -161,6 +170,7 @@ def procesar(texto, chat_id):
|
|||||||
"<b>tv <mensaje></b> — mensaje rápido a la tele\n"
|
"<b>tv <mensaje></b> — mensaje rápido a la tele\n"
|
||||||
"<b>alarma <mensaje></b> — secuencia completa (sirena x3)\n"
|
"<b>alarma <mensaje></b> — secuencia completa (sirena x3)\n"
|
||||||
"<b>youtube <URL></b> — reproduce YouTube y apaga al terminar\n"
|
"<b>youtube <URL></b> — reproduce YouTube y apaga al terminar\n"
|
||||||
|
"<b>descargar <enlace></b> — añade a Download Station del NAS\n"
|
||||||
"<b>panorama</b> — fotos de las cámaras\n"
|
"<b>panorama</b> — fotos de las cámaras\n"
|
||||||
"<b>limpieza</b> — limpia procesos atascados de la Pi\n"
|
"<b>limpieza</b> — limpia procesos atascados de la Pi\n"
|
||||||
"<b>reboot</b> — reinicia la Pi (necesita confirmar con /confirmar-reboot)\n"
|
"<b>reboot</b> — reinicia la Pi (necesita confirmar con /confirmar-reboot)\n"
|
||||||
|
|||||||
Reference in New Issue
Block a user