From 07f4f89a2257a25f08d9f70ac9da1f0afe238279 Mon Sep 17 00:00:00 2001 From: juanjo Date: Thu, 3 Sep 2026 23:38:23 +0200 Subject: [PATCH] 03-sep: [comando] /descargar ver progreso de descargas + listar_descargas en nas_dl --- vigilancia/nas_dl.py | 64 ++++++++++++++++++++++++++++++++++ vigilancia/pi_bot.py | 81 ++++++++++++++++++++++++-------------------- 2 files changed, 109 insertions(+), 36 deletions(-) diff --git a/vigilancia/nas_dl.py b/vigilancia/nas_dl.py index 6db70a2..1a3863f 100644 --- a/vigilancia/nas_dl.py +++ b/vigilancia/nas_dl.py @@ -139,6 +139,70 @@ def add_torrent(uri, session=None): return False +def human_size(n): + """Formatea bytes a una unidad legible.""" + n = float(n or 0) + for u in ("B", "KB", "MB", "GB", "TB"): + if n < 1024: + return f"{n:.1f}{u}" + n /= 1024 + return f"{n:.1f}PB" + + +def listar_descargas(hablar_solo_activas=True, session=None): + """Devuelve (lineas_texto, activas). Lista las tareas de Download Station + con estado, porcentaje de progreso, tamaño descargado y velocidad. + `progress` se calcula con downloaded_pieces/total_pieces (fiable en NAS).""" + if not session: + session = login() + if not session: + return ["❌ No se pudo autenticar con Download Station."], [] + try: + r = _api("/webapi/DownloadStation/task.cgi", { + "api": "SYNO.DownloadStation.Task", + "version": "3", + "method": "list", + "additional": "detail,transfer", + }, session=session) + if not r.get("success"): + return [f"❌ Error al listar: {r.get('error')}"], [] + except Exception as e: + return [f"❌ Error al consultar el NAS: {e}"], [] + tareas = r.get("data", {}).get("tasks", []) or [] + lineas, activas, resto = [], [], [] + for t in tareas: + titulo = t.get("title") or "" + estado = t.get("status") or "?" + detail = (t.get("additional") or {}).get("detail") or {} + transfer = (t.get("additional") or {}).get("transfer") or {} + total_pieces = detail.get("total_pieces") or 0 + done_pieces = transfer.get("downloaded_pieces") or 0 + pct = (done_pieces * 100.0 / total_pieces) if total_pieces else 0 + size = transfer.get("size_downloaded") or 0 + speed = transfer.get("speed_download") or 0 + if estado == "downloading": + activas.append(f"▶️ {pct:.1f}% {human_size(size)} ⬇️{speed // 1024} KB/s\n" + f" {titulo}") + else: + resto.append((estado, pct, size, titulo)) + if activas: + lineas.append("📥 DESCARGANDO") + lineas.extend(activas) + lineas.append("") + if not hablar_solo_activas: + lineas.append("📁 Otras tareas:") + for estado, pct, size, titulo in resto: + icono = {"finished": "✅", "error": "❌", "paused": "⏸️", + "seeding": "🌱"}.get(estado, "•") + if estado == "finished": + lineas.append(f"{icono} {titulo}") + else: + lineas.append(f"{icono} {estado} ({pct:.0f}%) — {titulo}") + if not activas: + lineas.append("No hay torrents descargándose ahora mismo.") + return lineas, activas + + if __name__ == "__main__": import sys if len(sys.argv) < 2: diff --git a/vigilancia/pi_bot.py b/vigilancia/pi_bot.py index a7085a4..5b22e87 100644 --- a/vigilancia/pi_bot.py +++ b/vigilancia/pi_bot.py @@ -80,17 +80,20 @@ def estado(): def enviar(texto, chat_id): - try: - r = requests.post( - f"https://api.telegram.org/bot{TOKEN}/sendMessage", - data={"chat_id": chat_id, "text": texto, "parse_mode": "HTML"}, - timeout=10, - ) - d = r.json() - if d.get("ok"): - return d["result"].get("message_id") - except Exception as e: - print("Error enviando:", e) + # Trocea mensajes largos (Telegram limita a ~4096 chars) para no fallar en silencio + for i in range(0, len(texto), 4000): + bloque = texto[i:i + 4000] + try: + r = requests.post( + f"https://api.telegram.org/bot{TOKEN}/sendMessage", + data={"chat_id": chat_id, "text": bloque, "parse_mode": "HTML"}, + timeout=10, + ) + d = r.json() + if d.get("ok"): + return d["result"].get("message_id") + except Exception as e: + print("Error enviando:", e) return None @@ -204,6 +207,10 @@ 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 in ("descargar", "/descargar"): + import nas_dl + lineas, _act = nas_dl.listar_descargas(hablar_solo_activas=True) + enviar("\n".join(lineas), chat_id) elif t.startswith("descargar ") or t.startswith("/descargar "): uri = texto.split(" ", 1)[1].strip() if not uri: @@ -253,31 +260,33 @@ def procesar(texto, chat_id): " ✏️ Ej: /serie breaking bad\n" "7. novedades — últimas novedades del catálogo\n" " ✏️ Ej: /novedades\n\n" - "━━━ 🖥️ NAS / DESCARGAS ━━━\n" - "8. descargar <enlace> — añade a Download Station\n" - " ✏️ Ej: /descargar magnet:?xt=urn:btih:...\n\n" - "━━━ 📺 TV ━━━\n" - "9. tv <mensaje> — mensaje rápido a la tele\n" - " ✏️ Ej: /tv la cena está lista\n" - "10. alarma <mensaje> — secuencia completa (sirena x3)\n" - " ✏️ Ej: /alarma vienen a por vosotros\n" - "11. youtube <URL> — reproduce YouTube en la TV\n" - " ✏️ Ej: /youtube https://youtu.be/xxxx\n\n" - "━━━ 🛠️ SISTEMA ━━━\n" - "12. estado — estado de la Pi\n" - " ✏️ Ej: /estado\n" - "13. temperatura — temperatura de la Pi\n" - " ✏️ Ej: /temperatura\n" - "14. panorama — fotos de las cámaras\n" - " ✏️ Ej: /panorama\n" - "15. limpieza — limpia procesos atascados\n" - " ✏️ Ej: /limpieza\n" - "16. servicios — estado de los servicios\n" - " ✏️ Ej: /servicios\n" - "17. reboot — reinicia la Pi\n" - " ✏️ Ej: /reboot → confirmar con /confirmar-reboot\n" - "18. ayuda — este índice\n" - " ✏️ Ej: /ayuda", + "━━━ 🖥️ NAS / DESCARGAS ━━━\n" + "8. descargar — torrents descargándose y su progreso\n" + " ✏️ Ej: /descargar\n" + "9. descargar <enlace> — añade a Download Station\n" + " ✏️ Ej: /descargar magnet:?xt=urn:btih:...\n\n" + "━━━ 📺 TV ━━━\n" + "10. tv <mensaje> — mensaje rápido a la tele\n" + " ✏️ Ej: /tv la cena está lista\n" + "11. alarma <mensaje> — secuencia completa (sirena x3)\n" + " ✏️ Ej: /alarma vienen a por vosotros\n" + "12. youtube <URL> — reproduce YouTube en la TV\n" + " ✏️ Ej: /youtube https://youtu.be/xxxx\n\n" + "━━━ 🛠️ SISTEMA ━━━\n" + "13. estado — estado de la Pi\n" + " ✏️ Ej: /estado\n" + "14. temperatura — temperatura de la Pi\n" + " ✏️ Ej: /temperatura\n" + "15. panorama — fotos de las cámaras\n" + " ✏️ Ej: /panorama\n" + "16. limpieza — limpia procesos atascados\n" + " ✏️ Ej: /limpieza\n" + "17. servicios — estado de los servicios\n" + " ✏️ Ej: /servicios\n" + "18. reboot — reinicia la Pi\n" + " ✏️ Ej: /reboot → confirmar con /confirmar-reboot\n" + "19. ayuda — este índice\n" + " ✏️ Ej: /ayuda", chat_id) elif t in ("servicios", "/servicios"): estado_svc = sh("systemctl is-active pi-bot tv-api autossh-tunel")