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!")