From aff68c3609d9c8cc093c8ace1e9a4fe380e017bf Mon Sep 17 00:00:00 2001 From: juanjo Date: Thu, 3 Sep 2026 22:37:48 +0200 Subject: [PATCH] 03-sep: [feature] catalogo por genero: /genero -> tipo -> genero -> 30 mas descargadas -> /torrents -> /bajar --- videoclub/global_buscar.py | 83 ++++++++++++++++++++++++++++++++++++++ vigilancia/pi_bot.py | 74 ++++++++++++++++++++++++++++++++- 2 files changed, 156 insertions(+), 1 deletion(-) diff --git a/videoclub/global_buscar.py b/videoclub/global_buscar.py index 8ee9084..3507ebd 100644 --- a/videoclub/global_buscar.py +++ b/videoclub/global_buscar.py @@ -27,6 +27,15 @@ sys.path.insert(0, str(BASE)) CINEMETA = "https://v3-cinemeta.strem.io" TORRENTIO = "https://torrentio.strem.fun/language=spanish" +# Catalogo por genero (Cinemeta). "top" = mas descargadas, "imdbRating" = mejor valoradas. +CINEMETA_CAT = "https://cinemeta-catalogs.strem.io" +CATEGORIAS = ("top", "imdbRating") +GENEROS = ( + "Action", "Adventure", "Animation", "Biography", "Comedy", "Crime", + "Documentary", "Drama", "Family", "Fantasy", "History", "Horror", + "Mystery", "Romance", "Sci-Fi", "Sport", "Thriller", "War", "Western", +) + HEADERS = {"User-Agent": "Mozilla/5.0"} # Utilidades de clasificacion reutilizadas de videoclub_buscador @@ -117,6 +126,65 @@ def torrents_de(imdb_id, tipo="movie"): return torrents +def generos_disponibles(): + """Devuelve la lista de generos con su numero (1-based) para el usuario.""" + return list(enumerate(GENEROS, 1)) + + +def catalogo_por_genero(tipo, genero, categoria="top", limite=30): + """'las N mas descargadas/valoradas' de un genero y tipo. + tipo: movie|series. genero: nombre en ingles (Action, Horror...). + categoria: top (mas descargadas) | imdbRating (mejor valoradas). + Devuelve lista de {name, type, imdb_id, year}.""" + if categoria not in CATEGORIAS: + categoria = "top" + if genero not in GENEROS: + return [] + url = (f"{CINEMETA_CAT}/{categoria}/catalog/{tipo}/{categoria}/" + f"genre={urllib.parse.quote(genero)}&skip=0.json") + try: + metas = _get_json(url).get("metas", []) + except Exception: + return [] + lista = [] + for m in metas: + imdb = m.get("id", "") + if not re.match(r"^tt\d+$", imdb): + continue + name = m.get("name", "") + year = m.get("year") or m.get("releaseInfo") or None + lista.append({ + "name": name, + "type": tipo, + "imdb_id": imdb, + "year": str(year) if year else None, + }) + return lista[:limite] + + +def formatear_generos(): + """Numeros la lista de generos para elegir por numero en Telegram.""" + lineas = ["🎭 Elige un genero por numero:", ""] + for n, g in generos_disponibles(): + lineas.append(f"{n}. {g}") + return vcb.trocear_texto(lineas) + + +def formatear_catalogo(resultados, tipo, genero, categoria): + """Numera los titulos de un catalogo por genero para elegir luego /torrents.""" + if not resultados: + return [f"No hay resultados para {genero}."] + etiqueta = "🎬 Peliculas" if tipo == "movie" else "📺 Series" + orden = "mas descargadas" if categoria == "top" else "mejor valoradas" + lineas = [f"{etiqueta} · {genero} — {len(resultados)} {orden}:", + "Responde /torrents <numero> para ver torrents.", ""] + for i, r in enumerate(resultados, 1): + icono = "📺" if r["type"] == "series" else "🎬" + y = f" ({r['year']})" if r.get("year") else "" + lineas.append(f"{i}. {icono} {r['name']}{y}") + return vcb.trocear_texto(lineas) + + def _seeders(title): m = re.search(r"👤\s*(\d+)", title) return int(m.group(1)) if m else 0 @@ -214,6 +282,8 @@ if __name__ == "__main__": print("Uso:") print(" python global_buscar.py busca ") print(" python global_buscar.py torrents [movie|series]") + print(" python global_buscar.py generos") + print(" python global_buscar.py catalogo [top|imdbRating] [limite]") sys.exit(1) cmd = sys.argv[1] @@ -230,3 +300,16 @@ if __name__ == "__main__": for m in formatear_torrents(res, imdb): print(m) print("---") + elif cmd == "generos": + for m in formatear_generos(): + print(m) + print("---") + elif cmd == "catalogo": + tipo = sys.argv[2] + genero = sys.argv[3] + cat = sys.argv[4] if len(sys.argv) > 4 else "top" + lim = int(sys.argv[5]) if len(sys.argv) > 5 else 30 + res = catalogo_por_genero(tipo, genero, cat, lim) + for m in formatear_catalogo(res, tipo, genero, cat): + print(m) + print("---") diff --git a/vigilancia/pi_bot.py b/vigilancia/pi_bot.py index c652644..ae2ed81 100644 --- a/vigilancia/pi_bot.py +++ b/vigilancia/pi_bot.py @@ -11,6 +11,8 @@ import requests # Estado por chat para la busqueda global interactiva (numero -> descarga) ultima_titulos = {} # chat_id -> {'query':..., 'results':[...]} ultimos_torrents = {} # chat_id -> {'titulo':..., 'torrents':[...]} +flujo_genero = {} # chat_id -> 'esperando_tipo' | 'esperando_genero' | None +flujo_genero_tipo = {} # chat_id -> 'movie' | 'series' (tipo ya elegido) BASE = os.path.dirname(os.path.abspath(__file__)) LOG = os.path.join(BASE, "bot.log") @@ -127,7 +129,35 @@ def mandar_youtube(url): def procesar(texto, chat_id): t = texto.lower().strip() log(f"RECIBIDO chat={chat_id} texto={texto!r}") - if t in ("estado", "/estado", "status", "/status"): + # Flujo interactivo por genero (espera numeros en pasos) + if flujo_genero.get(chat_id) and t.isdigit(): + paso = flujo_genero[chat_id] + if paso == "esperando_tipo": + if t == "1": + flujo_genero_tipo[chat_id] = "movie" + flujo_genero[chat_id] = "esperando_genero" + _pedir_genero(chat_id, "🎬") + elif t == "2": + flujo_genero_tipo[chat_id] = "series" + flujo_genero[chat_id] = "esperando_genero" + _pedir_genero(chat_id, "📺") + else: + enviar("Opción inválida. Responde 1 para Películas o 2 para Series.", chat_id) + elif paso == "esperando_genero": + n = int(t) + gol = _import_global_buscar() + if 1 <= n <= len(gol.GENEROS): + genero = gol.GENEROS[n - 1] + tipo = flujo_genero_tipo.get(chat_id, "movie") + flujo_genero.pop(chat_id, None) + flujo_genero_tipo.pop(chat_id, None) + _mostrar_catalogo_genero(chat_id, tipo, genero, "top") + else: + enviar("Número de género inválido.", chat_id) + return + if t in ("genero", "/genero"): + procesar_genero(chat_id) + elif t in ("estado", "/estado", "status", "/status"): msg, _ = estado() enviar(msg, chat_id) elif "temperatura" in t or "temp" in t: @@ -216,6 +246,7 @@ def procesar(texto, chat_id): "serie <nombre> — buscar serie con temporadas/capitulos\n" "novedades — ultimas novedades del catalogo\n" "busca <nombre> — buscar en TODO internet (miles de pelis)\n" + "genero — catálogo por género (30 más descargadas)\n" "torrents <n> — ver torrents del resultado n de /busca\n" "bajar <n> — descargar al NAS el torrent n de /torrents\n" "panorama — fotos de las cámaras\n" @@ -425,6 +456,47 @@ def procesar_busca(nombre, chat_id): log(f"BUSCA global '{nombre}': {len(resultados)} titulos") +def procesar_genero(chat_id): + """Comando '/genero': inicia el flujo por genero. Pide el tipo.""" + flujo_genero[chat_id] = "esperando_tipo" + flujo_genero_tipo.pop(chat_id, None) + enviar("🎭 Catalogo por genero (30 mas descargadas). Primero elige el tipo:\n" + "1. 🎬 Películas\n" + "2. 📺 Series\n\nResponde el 1 o el 2.", chat_id) + + +def _pedir_genero(chat_id, icono): + try: + gol = _import_global_buscar() + except Exception as e: + log(f"global_buscar import error: {e}") + enviar("❌ No se pudo cargar el modulo", chat_id) + return + for m in gol.formatear_generos(): + # anadimos el icono de tipo en la cabecera + if m.startswith("🎭"): + m = f"{icono} {m[1:]}" + enviar(m, chat_id) + enviar("Responde el numero del genero.", chat_id) + + +def _mostrar_catalogo_genero(chat_id, tipo, genero, categoria): + enviar(f"🔎 Consultando {genero} ({'peliculas' if tipo=='movie' else 'series'})...", chat_id) + try: + gol = _import_global_buscar() + resultados = gol.catalogo_por_genero(tipo, genero, categoria, limite=30) + mensajes = gol.formatear_catalogo(resultados, tipo, genero, categoria) + except Exception as e: + log(f"catalogo genero error: {e}") + enviar(f"❌ Error consultando el catalogo: {e}", chat_id) + return + for m in mensajes: + enviar(m, chat_id) + if resultados: + ultima_titulos[chat_id] = {"query": f"{genero} ({tipo})", "results": resultados} + log(f"GENERO {tipo} '{genero}': {len(resultados)} titulos") + + def procesar_torrents(n, chat_id): """Muestra los torrents (Torrentio) del titulo numero n de la ultima busqueda.""" estado = ultima_titulos.get(chat_id)