from httpx import AsyncClient
from html import escape
from urllib.parse import quote
from .. import LOGGER
from ..core.config_manager import Config
from ..core.torrent_manager import TorrentManager
from ..helper.ext_utils.bot_utils import new_task
from ..helper.ext_utils.status_utils import get_readable_file_size
from ..helper.ext_utils.telegraph_helper import telegraph
from ..helper.telegram_helper.button_build import ButtonMaker
from ..helper.telegram_helper.message_utils import edit_message, send_message
PLUGINS = []
SITES = None
TELEGRAPH_LIMIT = 300
async def initiate_search_tools():
if Config.DISABLE_TORRENTS:
LOGGER.warning("Torrents are disabled. Skipping search plugin initialization.")
return
qb_plugins = await TorrentManager.qbittorrent.search.plugins()
if qb_plugins:
names = [plugin.name for plugin in qb_plugins]
await TorrentManager.qbittorrent.search.uninstall_plugin(names)
PLUGINS.clear()
if Config.SEARCH_PLUGINS:
await TorrentManager.qbittorrent.search.install_plugin(Config.SEARCH_PLUGINS)
if Config.SEARCH_API_LINK:
global SITES
try:
async with AsyncClient() as client:
response = await client.get(f"{Config.SEARCH_API_LINK}/api/v1/sites")
data = response.json()
SITES = {
str(site): str(site).capitalize() for site in data["supported_sites"]
}
SITES["all"] = "All"
except Exception as e:
LOGGER.error(
f"{e} Can't fetching sites from SEARCH_API_LINK make sure use latest version of API"
)
SITES = None
async def search(key, site, message, method):
if method.startswith("api"):
if method == "apisearch":
LOGGER.info(f"API Searching: {key} from {site}")
if site == "all":
api = f"{Config.SEARCH_API_LINK}/api/v1/all/search?query={key}&limit={Config.SEARCH_LIMIT}"
else:
api = f"{Config.SEARCH_API_LINK}/api/v1/search?site={site}&query={key}&limit={Config.SEARCH_LIMIT}"
elif method == "apitrend":
LOGGER.info(f"API Trending from {site}")
if site == "all":
api = f"{Config.SEARCH_API_LINK}/api/v1/all/trending?limit={Config.SEARCH_LIMIT}"
else:
api = f"{Config.SEARCH_API_LINK}/api/v1/trending?site={site}&limit={Config.SEARCH_LIMIT}"
elif method == "apirecent":
LOGGER.info(f"API Recent from {site}")
if site == "all":
api = f"{Config.SEARCH_API_LINK}/api/v1/all/recent?limit={Config.SEARCH_LIMIT}"
else:
api = f"{Config.SEARCH_API_LINK}/api/v1/recent?site={site}&limit={Config.SEARCH_LIMIT}"
try:
async with AsyncClient() as client:
response = await client.get(api)
search_results = response.json()
if "error" in search_results or search_results["total"] == 0:
await edit_message(
message,
f"No result found for {key}\nTorrent Site:- {SITES.get(site)}",
)
return
msg = f"Found {min(search_results['total'], TELEGRAPH_LIMIT)}"
if method == "apitrend":
msg += f" trending result(s)\nTorrent Site:- {SITES.get(site)}"
elif method == "apirecent":
msg += (
f" recent result(s)\nTorrent Site:- {SITES.get(site)}"
)
else:
msg += f" result(s) for {key}\nTorrent Site:- {SITES.get(site)}"
search_results = search_results["data"]
except Exception as e:
await edit_message(message, str(e))
return
else:
LOGGER.info(f"PLUGINS Searching: {key} from {site}")
search = await TorrentManager.qbittorrent.search.start(
pattern=key, plugins=[site], category="all"
)
search_id = search.id
while True:
result_status = await TorrentManager.qbittorrent.search.status(search_id)
status = result_status[0].status
if status != "Running":
break
dict_search_results = await TorrentManager.qbittorrent.search.results(
id=search_id, limit=TELEGRAPH_LIMIT
)
search_results = dict_search_results.results
total_results = dict_search_results.total
if total_results == 0:
await edit_message(
message,
f"No result found for {key}\nTorrent Site:- {site.capitalize()}",
)
return
msg = f"Found {min(total_results, TELEGRAPH_LIMIT)}"
msg += f" result(s) for {key}\nTorrent Site:- {site.capitalize()}"
await TorrentManager.qbittorrent.search.delete(search_id)
link = await get_result(search_results, key, message, method)
buttons = ButtonMaker()
buttons.url_button("🔎 VIEW", link)
button = buttons.build_menu(1)
await edit_message(message, msg, button)
async def get_result(search_results, key, message, method):
telegraph_content = []
if method == "apirecent":
msg = "
API Recent Results
"
elif method == "apisearch":
msg = f"API Search Result(s) For {key}
"
elif method == "apitrend":
msg = "API Trending Results
"
else:
msg = f"PLUGINS Search Result(s) For {key}
"
for index, result in enumerate(search_results, start=1):
if method.startswith("api"):
try:
if "name" in result.keys():
msg += f"{escape(result['name'])}
"
if "torrents" in result.keys():
for subres in result["torrents"]:
msg += f"Quality: {subres['quality']} | Type: {subres['type']} | "
msg += f"Size: {subres['size']}
"
if "torrent" in subres.keys():
msg += f"Direct Link
"
elif "magnet" in subres.keys():
msg += "Share Magnet to "
msg += f"Telegram
"
msg += "
"
else:
msg += f"Size: {result['size']}
"
try:
msg += f"Seeders: {result['seeders']} | Leechers: {result['leechers']}
"
except Exception:
pass
if "torrent" in result.keys():
msg += f"Direct Link
"
elif "magnet" in result.keys():
msg += "Share Magnet to "
msg += f"Telegram
"
else:
msg += "
"
except Exception:
continue
else:
msg += f"{escape(result.fileName)}
"
msg += f"Size: {get_readable_file_size(result.fileSize)}
"
msg += f"Seeders: {result.nbSeeders} | Leechers: {result.nbLeechers}
"
link = result.fileUrl
if link.startswith("magnet:"):
msg += f"Share Magnet to Telegram
"
else:
msg += f"Direct Link
"
if len(msg.encode("utf-8")) > 39000:
telegraph_content.append(msg)
msg = ""
if index == TELEGRAPH_LIMIT:
break
if msg != "":
telegraph_content.append(msg)
await edit_message(
message, f"Creating {len(telegraph_content)} Telegraph pages."
)
path = [
(
await telegraph.create_page(
title="Mirror-leech-bot Torrent Search", content=content
)
)["path"]
for content in telegraph_content
]
if len(path) > 1:
await edit_message(
message, f"Editing {len(telegraph_content)} Telegraph pages."
)
await telegraph.edit_telegraph(path, telegraph_content)
return f"https://telegra.ph/{path[0]}"
def api_buttons(user_id, method):
buttons = ButtonMaker()
for data, name in SITES.items():
buttons.data_button(name, f"torser {user_id} {data} {method}")
buttons.data_button("Cancel", f"torser {user_id} cancel")
return buttons.build_menu(2)
async def plugin_buttons(user_id):
buttons = ButtonMaker()
if not PLUGINS:
pl = await TorrentManager.qbittorrent.search.plugins()
for i in pl:
PLUGINS.append(i.name)
for siteName in PLUGINS:
buttons.data_button(
siteName.capitalize(), f"torser {user_id} {siteName} plugin"
)
buttons.data_button("All", f"torser {user_id} all plugin")
buttons.data_button("Cancel", f"torser {user_id} cancel")
return buttons.build_menu(2)
@new_task
async def torrent_search(_, message):
user_id = message.from_user.id
buttons = ButtonMaker()
key = message.text.split()
if SITES is None and not Config.SEARCH_PLUGINS:
await send_message(
message, "No API link or search PLUGINS added for this function"
)
elif len(key) == 1 and SITES is None:
await send_message(message, "Send a search key along with command")
elif len(key) == 1:
buttons.data_button("Trending", f"torser {user_id} apitrend")
buttons.data_button("Recent", f"torser {user_id} apirecent")
buttons.data_button("Cancel", f"torser {user_id} cancel")
button = buttons.build_menu(2)
await send_message(message, "Send a search key along with command", button)
elif SITES is not None and Config.SEARCH_PLUGINS:
buttons.data_button("Api", f"torser {user_id} apisearch")
buttons.data_button("Plugins", f"torser {user_id} plugin")
buttons.data_button("Cancel", f"torser {user_id} cancel")
button = buttons.build_menu(2)
await send_message(message, "Choose tool to search:", button)
elif SITES is not None:
button = api_buttons(user_id, "apisearch")
await send_message(message, "Choose site to search | API:", button)
else:
button = await plugin_buttons(user_id)
await send_message(message, "Choose site to search | Plugins:", button)
@new_task
async def torrent_search_update(_, query):
user_id = query.from_user.id
message = query.message
key = message.reply_to_message.text.split(maxsplit=1)
key = key[1].strip() if len(key) > 1 else None
data = query.data.split()
if user_id != int(data[1]):
await query.answer("Not Yours!", show_alert=True)
elif data[2].startswith("api"):
await query.answer()
button = api_buttons(user_id, data[2])
await edit_message(message, "Choose site:", button)
elif data[2] == "plugin":
await query.answer()
button = await plugin_buttons(user_id)
await edit_message(message, "Choose site:", button)
elif data[2] != "cancel":
await query.answer()
site = data[2]
method = data[3]
if method.startswith("api"):
if key is None:
if method == "apirecent":
endpoint = "Recent"
elif method == "apitrend":
endpoint = "Trending"
await edit_message(
message,
f"Listing {endpoint} Items...\nTorrent Site:- {SITES.get(site)}",
)
else:
await edit_message(
message,
f"Searching for {key}\nTorrent Site:- {SITES.get(site)}",
)
else:
await edit_message(
message,
f"Searching for {key}\nTorrent Site:- {site.capitalize()}",
)
await search(key, site, message, method)
else:
await query.answer()
await edit_message(message, "Search has been canceled!")