03-sep: [feature] catalogo por genero: /genero -> tipo -> genero -> 30 mas descargadas -> /torrents -> /bajar
This commit is contained in:
@@ -27,6 +27,15 @@ sys.path.insert(0, str(BASE))
|
|||||||
CINEMETA = "https://v3-cinemeta.strem.io"
|
CINEMETA = "https://v3-cinemeta.strem.io"
|
||||||
TORRENTIO = "https://torrentio.strem.fun/language=spanish"
|
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"}
|
HEADERS = {"User-Agent": "Mozilla/5.0"}
|
||||||
|
|
||||||
# Utilidades de clasificacion reutilizadas de videoclub_buscador
|
# Utilidades de clasificacion reutilizadas de videoclub_buscador
|
||||||
@@ -117,6 +126,65 @@ def torrents_de(imdb_id, tipo="movie"):
|
|||||||
return torrents
|
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 = ["🎭 <b>Elige un genero por numero:</b>", ""]
|
||||||
|
for n, g in generos_disponibles():
|
||||||
|
lineas.append(f"<b>{n}.</b> {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 <b>{genero}</b>."]
|
||||||
|
etiqueta = "🎬 Peliculas" if tipo == "movie" else "📺 Series"
|
||||||
|
orden = "mas descargadas" if categoria == "top" else "mejor valoradas"
|
||||||
|
lineas = [f"{etiqueta} · <b>{genero}</b> — {len(resultados)} {orden}:",
|
||||||
|
"Responde <b>/torrents <numero></b> 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"<b>{i}.</b> {icono} {r['name']}{y}")
|
||||||
|
return vcb.trocear_texto(lineas)
|
||||||
|
|
||||||
|
|
||||||
def _seeders(title):
|
def _seeders(title):
|
||||||
m = re.search(r"👤\s*(\d+)", title)
|
m = re.search(r"👤\s*(\d+)", title)
|
||||||
return int(m.group(1)) if m else 0
|
return int(m.group(1)) if m else 0
|
||||||
@@ -214,6 +282,8 @@ if __name__ == "__main__":
|
|||||||
print("Uso:")
|
print("Uso:")
|
||||||
print(" python global_buscar.py busca <texto>")
|
print(" python global_buscar.py busca <texto>")
|
||||||
print(" python global_buscar.py torrents <imdb_id> [movie|series]")
|
print(" python global_buscar.py torrents <imdb_id> [movie|series]")
|
||||||
|
print(" python global_buscar.py generos")
|
||||||
|
print(" python global_buscar.py catalogo <movie|series> <genero> [top|imdbRating] [limite]")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
cmd = sys.argv[1]
|
cmd = sys.argv[1]
|
||||||
@@ -230,3 +300,16 @@ if __name__ == "__main__":
|
|||||||
for m in formatear_torrents(res, imdb):
|
for m in formatear_torrents(res, imdb):
|
||||||
print(m)
|
print(m)
|
||||||
print("---")
|
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("---")
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import requests
|
|||||||
# Estado por chat para la busqueda global interactiva (numero -> descarga)
|
# Estado por chat para la busqueda global interactiva (numero -> descarga)
|
||||||
ultima_titulos = {} # chat_id -> {'query':..., 'results':[...]}
|
ultima_titulos = {} # chat_id -> {'query':..., 'results':[...]}
|
||||||
ultimos_torrents = {} # chat_id -> {'titulo':..., 'torrents':[...]}
|
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__))
|
BASE = os.path.dirname(os.path.abspath(__file__))
|
||||||
LOG = os.path.join(BASE, "bot.log")
|
LOG = os.path.join(BASE, "bot.log")
|
||||||
@@ -127,7 +129,35 @@ def mandar_youtube(url):
|
|||||||
def procesar(texto, chat_id):
|
def procesar(texto, chat_id):
|
||||||
t = texto.lower().strip()
|
t = texto.lower().strip()
|
||||||
log(f"RECIBIDO chat={chat_id} texto={texto!r}")
|
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 <b>1</b> para Películas o <b>2</b> 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()
|
msg, _ = estado()
|
||||||
enviar(msg, chat_id)
|
enviar(msg, chat_id)
|
||||||
elif "temperatura" in t or "temp" in t:
|
elif "temperatura" in t or "temp" in t:
|
||||||
@@ -216,6 +246,7 @@ def procesar(texto, chat_id):
|
|||||||
"<b>serie <nombre></b> — buscar serie con temporadas/capitulos\n"
|
"<b>serie <nombre></b> — buscar serie con temporadas/capitulos\n"
|
||||||
"<b>novedades</b> — ultimas novedades del catalogo\n"
|
"<b>novedades</b> — ultimas novedades del catalogo\n"
|
||||||
"<b>busca <nombre></b> — buscar en TODO internet (miles de pelis)\n"
|
"<b>busca <nombre></b> — buscar en TODO internet (miles de pelis)\n"
|
||||||
|
"<b>genero</b> — catálogo por género (30 más descargadas)\n"
|
||||||
"<b>torrents <n></b> — ver torrents del resultado n de /busca\n"
|
"<b>torrents <n></b> — ver torrents del resultado n de /busca\n"
|
||||||
"<b>bajar <n></b> — descargar al NAS el torrent n de /torrents\n"
|
"<b>bajar <n></b> — descargar al NAS el torrent n de /torrents\n"
|
||||||
"<b>panorama</b> — fotos de las cámaras\n"
|
"<b>panorama</b> — fotos de las cámaras\n"
|
||||||
@@ -425,6 +456,47 @@ def procesar_busca(nombre, chat_id):
|
|||||||
log(f"BUSCA global '{nombre}': {len(resultados)} titulos")
|
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 <b>genero</b> (30 mas descargadas). Primero elige el tipo:\n"
|
||||||
|
"<b>1.</b> 🎬 Películas\n"
|
||||||
|
"<b>2.</b> 📺 Series\n\nResponde el <b>1</b> o el <b>2</b>.", 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 <b>numero</b> 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):
|
def procesar_torrents(n, chat_id):
|
||||||
"""Muestra los torrents (Torrentio) del titulo numero n de la ultima busqueda."""
|
"""Muestra los torrents (Torrentio) del titulo numero n de la ultima busqueda."""
|
||||||
estado = ultima_titulos.get(chat_id)
|
estado = ultima_titulos.get(chat_id)
|
||||||
|
|||||||
Reference in New Issue
Block a user