06-sep: [videoclub] temporada completa, rangos bajar X-Y, pelis solo castellano

This commit is contained in:
juanjo
2026-09-06 17:40:25 +02:00
parent d77a25cfb4
commit 086a97c3f9
2 changed files with 144 additions and 40 deletions

View File

@@ -51,7 +51,8 @@ CALIDAD_MALA = re.compile(
r"(CAMRip|TS\.|HDTS|Telesync|KinoRip|Screener|HDCAM|HC\b|r5|360p|240p)", r"(CAMRip|TS\.|HDTS|Telesync|KinoRip|Screener|HDCAM|HC\b|r5|360p|240p)",
re.IGNORECASE) re.IGNORECASE)
CASTELLANO = re.compile( CASTELLANO = re.compile(
r"(Esp|Castellano|Span|Spanish|dual|lat|\bES\b|audio)", re.IGNORECASE) r"(Esp[añ]ol|Castellano|Spanish|Latino|DualLat|\bdual\b|\bESS?\b|\bESP\b)",
re.IGNORECASE)
INGLES = re.compile(r"(Eng|English|Ingl[eé]s|\bEN\b)", re.IGNORECASE) INGLES = re.compile(r"(Eng|English|Ingl[eé]s|\bEN\b)", re.IGNORECASE)
OTRO_IDIOMA = re.compile( OTRO_IDIOMA = re.compile(
r"(Ita\b|MIRCrew|RO\s?EN|South Wind|Silver Zero|Francais|Deutsch|Portugu[eê]s)", r"(Ita\b|MIRCrew|RO\s?EN|South Wind|Silver Zero|Francais|Deutsch|Portugu[eê]s)",
@@ -193,7 +194,7 @@ def buscar_peliculas(termino, items=None):
continue continue
if not it["buena_calidad"]: if not it["buena_calidad"]:
continue continue
if not (it.get("castellano") or bool(INGLES.search(it.get("name", "")))): if not it.get("castellano"):
continue continue
clean = (it.get("clean") or "").lower() clean = (it.get("clean") or "").lower()
name = (it.get("name") or "").lower() name = (it.get("name") or "").lower()
@@ -294,11 +295,13 @@ def formatear_series(grupos):
etiqueta = f"S{temp:02d}E{cap:02d}" etiqueta = f"S{temp:02d}E{cap:02d}"
else: else:
etiqueta = it["clean"] etiqueta = it["clean"]
lineas.append(f" <b>{num_global}.</b> {etiqueta} — {resto}") idioma = " · cast." if it.get("castellano") else ""
lineas.append(f" <b>{num_global}.</b> {etiqueta} — {resto}{idioma}")
num_global += 1 num_global += 1
lineas.append("") lineas.append("")
lineas.append("Responde <b>/torrents NUMERO</b> de episodio para ver opciones y descargar.") lineas.append("Responde <b>/torrents NUMERO</b> de episodio para ver opciones y descargar.")
lineas.append("O <b>/temporada N</b> (temporada completa) o <b>/bajar X-Y</b> (rango).")
return trocear_texto(lineas) return trocear_texto(lineas)

View File

@@ -1,5 +1,6 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import os import os
import re
import subprocess import subprocess
import sys import sys
import threading import threading
@@ -800,8 +801,11 @@ def procesar(texto, chat_id):
n = _int_arg(texto) n = _int_arg(texto)
procesar_torrents(n, chat_id) procesar_torrents(n, chat_id)
elif t.startswith("bajar ") or t.startswith("/bajar "): elif t.startswith("bajar ") or t.startswith("/bajar "):
spec = texto.split(" ", 1)[1].strip() if " " in texto else ""
procesar_bajar(spec, chat_id)
elif t.startswith("temporada ") or t.startswith("/temporada "):
n = _int_arg(texto) n = _int_arg(texto)
procesar_bajar(n, chat_id) procesar_temporada(n, chat_id)
elif comando == "aspiradora": elif comando == "aspiradora":
procesar_aspiradora(argumento, chat_id) procesar_aspiradora(argumento, chat_id)
elif comando in ("menu", "menú", "inicio", "start") and not argumento: elif comando in ("menu", "menú", "inicio", "start") and not argumento:
@@ -824,7 +828,7 @@ def procesar(texto, chat_id):
"<b>3.</b> <code>torrents &lt;n&gt;</code> — torrents del resultado n\n" "<b>3.</b> <code>torrents &lt;n&gt;</code> — torrents del resultado n\n"
" ✏️ Ej: <code>/torrents 1</code>\n" " ✏️ Ej: <code>/torrents 1</code>\n"
"<b>4.</b> <code>bajar &lt;n&gt;</code> — descargar al NAS el torrent n\n" "<b>4.</b> <code>bajar &lt;n&gt;</code> — descargar al NAS el torrent n\n"
" ✏️ Ej: <code>/bajar 1</code>\n" " ✏️ Ej: <code>/bajar 1</code>, <code>/bajar 2-5</code>, <code>/temporada 1</code>\n"
"<b>5.</b> <code>pelicula &lt;nombre&gt;</code> — buscar película (catálogo)\n" "<b>5.</b> <code>pelicula &lt;nombre&gt;</code> — buscar película (catálogo)\n"
" ✏️ Ej: <code>/pelicula ant-man</code>\n" " ✏️ Ej: <code>/pelicula ant-man</code>\n"
"<b>6.</b> <code>serie &lt;nombre&gt;</code> — buscar serie (catálogo)\n" "<b>6.</b> <code>serie &lt;nombre&gt;</code> — buscar serie (catálogo)\n"
@@ -1274,51 +1278,148 @@ def procesar_torrents(n, chat_id):
log(f"TORRENTS '{r['name']}': {len(torrents)} torrents") log(f"TORRENTS '{r['name']}': {len(torrents)} torrents")
def procesar_bajar(n, chat_id): def procesar_bajar(spec, chat_id):
"""Descarga el torrent numero n al NAS (Download Station via magnet).""" """Descarga al NAS: numero, rango (2-5) o episodios directos."""
estado = ultimos_torrents.get(chat_id) estado = ultimos_torrents.get(chat_id)
if not estado or n is None or n < 1 or n > len(estado["torrents"]): titulos = ultima_titulos.get(chat_id) or {}
if estado and estado.get("torrents"):
idx = _parse_indices(spec, len(estado["torrents"]))
if not idx:
enviar("Uso: <b>/bajar &lt;numero&gt;</b> o <b>/bajar X-Y</b>.",
chat_id)
return
buenas, malas = [], []
for i in idx:
uri, etiqueta = _bajar_torrent(estado["torrents"][i - 1],
estado.get("titulo") or "")
if not uri:
malas.append(etiqueta)
continue
ok, _ = _bajar_uri(uri, etiqueta)
(buenas if ok else malas).append(etiqueta)
log(f"BAJAR '{estado.get('titulo')}' torrent {i}: ok={ok}")
partes = []
if buenas:
partes.append(f"📥 Añadidos al NAS ({len(buenas)}):\n" +
"\n".join(f"• {b}" for b in buenas))
if malas:
partes.append("❌ Fallaron:\n" +
"\n".join(f"• {m}" for m in malas))
enviar("\n".join(partes) or "❌ Nada que descargar.", chat_id)
return
if titulos.get("origen") == "catalogo" and titulos.get("results"):
resultados = titulos["results"]
idx = _parse_indices(spec, len(resultados))
if not idx:
enviar("Uso: <b>/bajar &lt;numero&gt;</b>, <b>/bajar X-Y</b> o "
"<b>/temporada N</b>.", chat_id)
return
buenas, malas = [], []
for i in idx:
it = resultados[i - 1]
nombre = it.get("clean") or it.get("name") or f"episodio {i}"
if not it.get("enlace"):
malas.append(f"{nombre} (sin enlace)")
continue
ok, _ = _bajar_uri(it["enlace"], nombre)
(buenas if ok else malas).append(nombre)
log(f"BAJAR catalogo '{nombre}': ok={ok}")
partes = []
if buenas:
partes.append(f"📥 Añadidos al NAS ({len(buenas)}):\n" +
"\n".join(f"• {b}" for b in buenas))
if malas:
partes.append("❌ Fallaron:\n" +
"\n".join(f"• {m}" for m in malas))
enviar("\n".join(partes) or "❌ Nada que descargar.", chat_id)
return
enviar("Primero usa <b>/busca</b>, <b>/pelicula</b>, <b>/serie</b>, " enviar("Primero usa <b>/busca</b>, <b>/pelicula</b>, <b>/serie</b>, "
"<b>/novedades</b> o <b>/genero</b>, luego " "<b>/novedades</b> o <b>/genero</b>, luego "
"<b>/torrents &lt;numero&gt;</b>.", chat_id) "<b>/torrents &lt;numero&gt;</b>.", chat_id)
return
t = estado["torrents"][n - 1]
if t.get("enlace") and not t.get("info_hash"): def _parse_indices(spec, total, maximo=10):
# Enlace directo de catalogo: al NAS sin magnet. """'3' -> [3]; '2-5' o '2:5' -> [2..5]. Recorta al total (max 10)."""
spec = str(spec or "").strip()
m = re.match(r"(\d+)\s*[-:]\s*(\d+)$", spec)
if m:
a, b = int(m.group(1)), int(m.group(2))
if a > b:
a, b = b, a
elif spec.isdigit():
a = b = int(spec)
else:
return []
a = max(a, 1)
b = min(b, total)
if a > b:
return []
return list(range(a, min(b, a + maximo - 1) + 1))
def _bajar_uri(uri, etiqueta):
"""Anade un magnet/enlace al NAS. Devuelve (ok, etiqueta)."""
try: try:
import nas_dl import nas_dl
ok = nas_dl.add_torrent(t["enlace"]) ok = nas_dl.add_torrent(uri)
except Exception as e: except Exception as e:
log(f"bajar error: {e}") log(f"bajar error: {e}")
enviar(f"❌ Error al anadir al NAS: {e}", chat_id) return False, f"{etiqueta} (error: {e})"
return return bool(ok), etiqueta
if ok:
enviar(f"📥 Añadido al NAS: <b>{t.get('filename') or estado['titulo']}</b>.",
chat_id) def _bajar_torrent(t, titulo):
else: """Resuelve un item de ultimos_torrents a (uri, etiqueta)."""
enviar("❌ No se pudo añadir al NAS.", chat_id) if t.get("enlace") and not t.get("info_hash"):
return return t["enlace"], t.get("filename") or titulo
try: try:
gol = _import_global_buscar() gol = _import_global_buscar()
except Exception as e: except Exception as e:
log(f"global_buscar import error: {e}") log(f"global_buscar import error: {e}")
enviar("❌ No se pudo cargar el modulo", chat_id) return None, "sin modulo"
tam = f"{t['size']:.1f}GB" if t.get("size") else "?"
return gol.magnet_de(t), f"{t.get('filename') or titulo} ({t.get('calidad')}, {tam})"
def procesar_temporada(n, chat_id):
"""Descarga la temporada N completa de la ultima serie del catalogo."""
titulos = ultima_titulos.get(chat_id) or {}
if titulos.get("origen") != "catalogo" or not titulos.get("results"):
enviar("Primero busca una <b>/serie</b> y luego "
"<b>/temporada &lt;N&gt;</b>.", chat_id)
return
if n is None or n < 1:
enviar("Uso: <b>/temporada &lt;N&gt;</b>.", chat_id)
return return
magnet = gol.magnet_de(t)
log(f"BAJAR '{estado['titulo']}' torrent {n}: {magnet[:60]}...")
try: try:
import nas_dl sys.path.insert(0, os.path.join(BASE, "..", "videoclub"))
ok = nas_dl.add_torrent(magnet) import videoclub_buscador as vcb
except Exception as e: except Exception as e:
log(f"bajar error: {e}") log(f"videoclub_buscador import error: {e}")
enviar(f"❌ Error al anadir al NAS: {e}", chat_id) enviar("❌ No se pudo cargar el modulo videoclub", chat_id)
return return
if ok: capitulos = []
tamaño = f"{t['size']:.1f}GB" if t.get("size") else "?" for it in titulos["results"]:
enviar(f"📥 Añadido al NAS: <b>{t['filename'] or estado['titulo']}</b> " try:
f"({t['calidad']}, {tamaño}).", chat_id) temp, _cap = vcb.parsear_episodio(it.get("name") or "")
else: except Exception:
enviar("❌ No se pudo añadir al NAS.", chat_id) temp = 0
if temp == n and it.get("enlace"):
capitulos.append(it)
if not capitulos:
enviar(f"❌ No hay capitulos con enlace en la temporada {n}.",
chat_id)
return
buenas, malas = [], []
for it in capitulos[:25]:
nombre = it.get("clean") or it.get("name") or "capitulo"
ok, _ = _bajar_uri(it["enlace"], nombre)
(buenas if ok else malas).append(nombre)
partes = [f"📥 Temporada {n} al NAS ({len(buenas)} capitulos)."]
if malas:
partes.append("❌ Fallaron: " + ", ".join(malas))
enviar("\n".join(partes), chat_id)
log(f"TEMPORADA {n}: {len(buenas)} ok, {len(malas)} fallo")
def monitor_temperatura(): def monitor_temperatura():