# VERSION: 1.1 # AUTHORS: github.com/444995 # Not the best code I've written, will prob get updated import re import gzip import tempfile import urllib.parse from urllib.error import HTTPError import http.cookiejar as cookielib from novaprinter import prettyPrinter # EVEN IF THIS IS SET TO FALSE # IT STILL FALLS BACK TO MAGNET LINKS # IF A TORRENT FILE IS NOT AVAILABLE USE_MAGNET_LINKS = False # FALSE IS RECOMMENDED class zooqle(object): url = 'https://zooqle.skin' name = 'Zooqle' supported_categories = { 'all': '0', 'anime': 'anime', 'movies': 'movies', 'tv': 'tv', 'music': 'music', 'games': 'games', 'software': 'apps', 'books': None, } search_url = f"{url}/search/" download_url = f"{url}/torfile/" torrent_page_url = f'{url}/torrent-page/' PATTERNS = { 'category': r'<li><a href="javascript:void\(\);" onclick="p([^"]+)\.submit\(\)" style="[^"]*">([^<]+)</a></li>', 'row': r'<tr>.*?</tr>', 'torrent_id': r'<input type="hidden" name="id" value="(\d+)"', 'name': r'<title>(.*?)\s+Torrent - Zooqle</title>', 'size': r'<i class="fa fa-file"></i>\s* \s*<strong>Size</strong>.*?</li>\s*<li>:</li>\s*<li>(.*?)</li>', 'seeds': r'<i class="fa fa-arrow-up"></i>\s* \s*<strong>Seed</strong>.*?</li>\s*<li>:</li>\s*<li[^>]*>(.*?)</li>', 'leech': r'<i class="fa fa-arrow-down"></i>\s* \s*<strong>Leech</strong>.*?</li>\s*<li>:</li>\s*<li[^>]*>(.*?)</li>', 'category_match': r'<i class="fa fa-tag"></i>\s* \s*<strong>Category</strong>.*?</li>\s*<li>:</li>\s*<li><a[^>]*>([^<]+)</a>', 'magnet_link': r'href="(magnet:\?xt=urn:btih:[^"]+)"', 'hid': r'<input type="hidden" name="hid" value="([^"]+)"' } def __init__(self): self.torrents_per_page = 40 self.cookiejar = cookielib.LWPCookieJar() self.opener = self._create_opener() def _create_opener(self): opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(self.cookiejar)) opener.addheaders = [ ('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36'), ('Content-Type', 'application/x-www-form-urlencoded'), ] return opener def _make_request(self, url, data=None, extra_headers=None): try: encoded_data = urllib.parse.urlencode(data).encode() if data else None request = urllib.request.Request(url, encoded_data) if extra_headers: for key, value in extra_headers.items(): request.add_header(key, value) with self.opener.open(request) as response: if response.info().get('Content-Encoding') == 'gzip': content = gzip.decompress(response.read()) else: content = response.read() return content except HTTPError as e: raise HTTPError(f"Request failed: {e}") def download_torrent(self, info): if USE_MAGNET_LINKS: return torrent_file = self._make_request( url=self.download_url, data={"hid": info} ) with tempfile.NamedTemporaryFile(suffix='.torrent', delete=False) as file: file.write(torrent_file) print(f"{file.name} {self.download_url}") def search(self, what, cat='all'): formatted_what = what.replace(" ", "+") category = self._establish_category_url(cat) page_num = 1 while True: response = self._make_request( url=f"{self.url}/query/{formatted_what}/page/{page_num}", data={'q': what, 'page': page_num}, extra_headers={ "referer": f'https://zooqle.skin/category/{category}/' } if category != '0' else {}, ).decode('utf-8', errors='ignore') num_results = self._parse_results(response, category) if num_results < self.torrents_per_page: return page_num += 1 def _establish_category_url(self, category): return self.supported_categories[category] def _parse_results(self, html_content, category): rows = re.findall(self.PATTERNS['row'], html_content, re.DOTALL) torrents_found = 0 for row in rows: torrent_id = self._get_torrent_id(row) if torrent_id is None: continue torrent_page = self._get_torrent_page(torrent_id) torrent_data = self._extract_torrent_data(torrent_page, category) if torrent_data: prettyPrinter(torrent_data) print("\n\n") torrents_found += 1 return torrents_found def _extract_torrent_data(self, torrent_page, category): name_elem = re.search(self.PATTERNS['name'], torrent_page, re.DOTALL) name = name_elem.group(1).strip() if name_elem else "N/A" size_elem = re.search(self.PATTERNS['size'], torrent_page, re.DOTALL) size = size_elem.group(1).strip() if size_elem else "N/A" seeds_elem = re.search(self.PATTERNS['seeds'], torrent_page, re.DOTALL) seeds = seeds_elem.group(1).strip() if seeds_elem else -1 leech_elem = re.search(self.PATTERNS['leech'], torrent_page, re.DOTALL) leech = leech_elem.group(1).strip() if leech_elem else -1 if category != '0': category_match = re.search(self.PATTERNS['category_match'], torrent_page, re.DOTALL).group(1).strip() if category_match.lower() != category: return None return { 'name': name, 'size': size, 'seeds': int(seeds) if str(seeds).isdigit() else -1, 'leech': int(leech) if str(leech).isdigit() else -1, 'link': self._get_download_link(torrent_page), 'desc_link': self.url, 'engine_url': self.url, } def _get_torrent_id(self, row): match = re.search(self.PATTERNS['torrent_id'], row) return match.group(1) if match else None def _get_torrent_page(self, torrent_id): return self._make_request( self.torrent_page_url, data={'id': torrent_id}, ).decode('utf-8', errors='ignore') def _get_download_link(self, torrent_page): magnet_link = None hid = None magnet_match = re.search(self.PATTERNS['magnet_link'], torrent_page) if magnet_match: magnet_link = magnet_match.group(1) if not USE_MAGNET_LINKS: hid_match = re.search(self.PATTERNS['hid'], torrent_page) if hid_match: hid = hid_match.group(1) if USE_MAGNET_LINKS or (not USE_MAGNET_LINKS and not hid): return magnet_link if magnet_link else 'N/A' else: return hid if __name__ == "__main__": engine = zooqle() engine.search("1080p", "all")