Files
biblioteca_conocimiento_lab…/vigilancia/pelis.py

217 lines
7.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
pelis.py — Listado de novedades con buena calidad en castellano del catalogo
RealDebrid/Torrentio, con el comando 'descargar <url>' ya montado para copiar
y pegar en Telegram. Lo usa el comando 'pelis' de pi_bot.py.
- Filtra: NO adulto, buena calidad, castellano.
- Trocea la salida en mensajes <= CHUNK_MAX para que Telegram no corte.
- Los items adultos se devuelven por separado (efimeros, nunca se guardan).
Autonomo (sin dependencias): usa solo urllib y la logica de clasificacion
duplicada de videoclub/videoclub.py para no depender del repo de negocio.
"""
import json
import os
import re
import urllib.request
from pathlib import Path
BASE = Path(__file__).resolve().parent
CHUNK_MAX = 3500 # limite de Telegram (~4096), con margen
# Reutiliza el .env del propio directorio (carga simple, sin python-dotenv)
ENV = {}
try:
for line in open(BASE / ".env"):
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
TORRENTIO_RD_TOKEN = os.getenv("TORRENTIO_RD_TOKEN") or ENV.get("TORRENTIO_RD_TOKEN", "")
TORRENTIO_CONFIG = os.getenv("TORRENTIO_CONFIG") or ENV.get("TORRENTIO_CONFIG", "language=spanish")
CATALOGO = ("https://torrentio.strem.fun/"
f"{TORRENTIO_CONFIG}|realdebrid={TORRENTIO_RD_TOKEN}/"
"catalog/other/torrentio-realdebrid.json")
YEAR_RE = re.compile(r"\((19\d\d|20\d\d)\)")
YEAR_BARE_RE = re.compile(r"\.(19\d\d|20\d\d)\.")
SERIE_RE = re.compile(r"(S\d{1,2}E\d{1,3}|Cap[\.\s]|cap[\.\s]|Temporada|temp\.|\bS0?1E\d|\dx\d\d)", re.IGNORECASE)
ADULTO_RE = re.compile(r"(\.XXX\.|XXX\.|private\.|brazzers|onlytarts|spankbang|xhamster|porntrex|missav|sexandsubmission|evilangel|monstersofcock|itsanal|thelifeerotic)", re.IGNORECASE)
SERIE_PALABRAS = {"star trek", "stargate", "heidi", "euphoria", "from", "the pitt",
"supergirl", "monarch", "paradise", "desaparecida", "vanished",
"strange new worlds", "star trek strange", "the studio",
"one punch man", "conan", "detective conan"}
EXTRA_SUCIO = ["wolfmax4k.com", "pctfenix", "pctfenix.com", "yts", "yts.mx",
"rarbg", "verpeliculasonline", "viruse", "proyecto", "kikorip",
"camrip", "telesync", "hdtv", "bluray", "remux", "webrip", "bdrip",
"repack", "proper", "xtrem"]
# Calidad mala -> se excluye de "buena calidad"
CALIDAD_MALA = re.compile(
r"(CAMRip|TS\.|HDTS|Telesync|KinoRip|Screener|HDCAM|HC\b|r5|telesync)", re.IGNORECASE)
# Idiomas que indican castellano/espanol/dual
CASTELLANO = re.compile(r"(Esp|Castellano|Span|dual|lat)", re.IGNORECASE)
def clean_filename(name):
"""Devuelve (titulo_limpio, year, tipo)."""
name = re.sub(r"\.(mkv|mp4|avi|mov|webm|m4v)$", "", name.strip(), flags=re.IGNORECASE)
year = None
m = YEAR_RE.search(name)
if m:
year = m.group(1)
name = name.replace(m.group(0), " ")
else:
m = YEAR_BARE_RE.search(name)
if m:
year = m.group(1)
name = name.replace(m.group(0), " ")
es_serie = bool(SERIE_RE.search(name)) or any(p in name.lower() for p in SERIE_PALABRAS)
es_adulto = bool(ADULTO_RE.search(name))
name = re.sub(r"\[[^\]]*\]", " ", name)
name = re.sub(r"[\(\)\[\]]", " ", name)
name = name.replace("_", " ").replace(".", " ")
name = re.sub(r"\s+", " ", name).strip()
tokens = [t for t in name.split(" ") if t.lower() not in EXTRA_SUCIO]
name = " ".join(tokens).strip(" -")
name = re.sub(r"\s+", " ", name).strip()
tipo = "adulto" if es_adulto else ("serie" if es_serie else "pelicula")
return name, year, tipo
def fetch_catalogo():
"""Descarga el catalogo + enlaces directos RD. Devuelve lista de items."""
if not TORRENTIO_RD_TOKEN:
return []
url = CATALOGO.format()
req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
with urllib.request.urlopen(req, timeout=40) as r:
data = json.loads(r.read().decode("utf-8"))
enlaces = {}
try:
req2 = urllib.request.Request(
"https://api.real-debrid.com/rest/1.0/torrents",
headers={"Authorization": f"Bearer {TORRENTIO_RD_TOKEN}"})
with urllib.request.urlopen(req2, timeout=40) as r2:
for t in json.loads(r2.read().decode("utf-8")):
links = t.get("links") or []
if links:
enlaces[t["id"]] = links[0]
except Exception as e:
print(f"[pelis] No se pudieron obtener enlaces RD: {e}")
items = []
for m in data.get("metas", []):
name = m.get("name", "")
clean, year, tipo = clean_filename(name)
rd_id = m.get("id", "").split(":", 1)[-1]
items.append({
"id": m.get("id"),
"name": name,
"clean": clean,
"year": year,
"tipo": tipo,
"enlace": enlaces.get(rd_id),
"buena_calidad": not bool(CALIDAD_MALA.search(name)),
"castellano": bool(CASTELLANO.search(name)),
})
return items
def seleccionar(items, ano_min=2024):
"""Novedades con buena calidad en castellano (excluye adulto y series sueltas)."""
out = []
for it in items:
if it["tipo"] == "adulto":
continue
if not it["buena_calidad"] or not it["castellano"]:
continue
try:
y = int(it["year"]) if it["year"] else 0
except ValueError:
y = 0
if y < ano_min:
continue
if it["tipo"] == "serie" and it.get("enlace") and "S01E0" in it["name"]:
continue
out.append(it)
out.sort(key=lambda x: (int(x["year"] or 0), x["clean"] or ""), reverse=True)
return out
def separar_adultos(items):
"""Separa los items adultos del resto."""
normales, adultos = [], []
for it in items:
(adultos if it["tipo"] == "adulto" else normales).append(it)
return normales, adultos
def formatear_linea(it):
"""Línea lista para copiar/pegar: titulo + 'descargar <url>'."""
y = f" ({it['year']})" if it["year"] else ""
enlace = it.get("enlace") or ""
linea = f"{it['clean']}{y}"
if enlace:
linea += f"\n<code>descargar {enlace}</code>"
return linea
def trocear(lista, max_len=CHUNK_MAX):
"""Convierte items en mensajes <= max_len. Cada item no se parte."""
mensajes, actual = [], []
long = 0
for it in lista:
linea = formatear_linea(it)
if actual and long + len(linea) + 1 > max_len:
mensajes.append("\n".join(actual))
actual, long = [], 0
actual.append(linea)
long += len(linea) + 1
if actual:
mensajes.append("\n".join(actual))
return mensajes
def trocear_efimero(texto, items, max_len=CHUNK_MAX):
"""Trocea un texto con lineas 'titulo + descargar <url>' sin partir lineas."""
lineas = texto.split("\n")
mensajes, actual = [], []
long = 0
for linea in lineas:
if actual and long + len(linea) + 1 > max_len:
mensajes.append("\n".join(actual))
actual, long = [], 0
actual.append(linea)
long += len(linea) + 1
if actual:
mensajes.append("\n".join(actual))
return mensajes
def generar(fecha=None, ano_min=2024):
"""Devuelve (mensajes_normales, adultos_list). Uso desde pi_bot."""
items = fetch_catalogo()
normales, adultos = separar_adultos(items)
sel = seleccionar(normales, ano_min=ano_min)
mensajes = trocear(sel)
if not mensajes:
mensajes = ["No hay novedades con buena calidad en castellano."]
return mensajes, adultos
if __name__ == "__main__":
msgs, adult = generar()
for i, m in enumerate(msgs, 1):
print(f"--- Mensaje {i}/{len(msgs)} ({len(m)} chars) ---")
print(m)
print()
print(f"ADULTOS: {len(adult)} (efimeros, no se listan aqui)")