def search(self, task, entry, config=None): """ Search for name from piratebay. """ if not isinstance(config, dict): config = {} self.set_urls(config.get('url', URL)) sort = SORT.get(config.get('sort_by', 'seeds')) if config.get('sort_reverse'): sort += 1 if isinstance(config.get('category'), int): category = config['category'] else: category = CATEGORIES.get(config.get('category', 'all')) filter_url = '/0/%d/%d' % (sort, category) entries = set() for search_string in entry.get('search_strings', [entry['title']]): query = normalize_unicode(search_string) # TPB search doesn't like dashes or quotes query = query.replace('-', ' ').replace("'", " ") # urllib.quote will crash if the unicode string has non ascii characters, so encode in utf-8 beforehand url = '%s/search/%s%s' % (self.url, quote(query.encode('utf-8')), filter_url) log.debug('Using %s as piratebay search url' % url) page = task.requests.get(url).content soup = get_soup(page) for link in soup.find_all('a', attrs={'class': 'detLink'}): entry = Entry() entry['title'] = self.extract_title(link) if not entry['title']: log.error('Malformed search result. No title or url found. Skipping.') continue href = link.get('href') if href.startswith('/'): # relative link? href = self.url + href entry['url'] = href tds = link.parent.parent.parent.find_all('td') entry['torrent_seeds'] = int(tds[-2].contents[0]) entry['torrent_leeches'] = int(tds[-1].contents[0]) entry['torrent_availability'] = torrent_availability( entry['torrent_seeds'], entry['torrent_leeches'] ) # Parse content_size size_text = link.find_next(attrs={'class': 'detDesc'}).get_text() if size_text: size = re.search(r'Size (\d+(\.\d+)?\xa0(?:[PTGMK])?i?B)', size_text) if size: entry['content_size'] = parse_filesize(size.group(1)) else: log.error( 'Malformed search result? Title: "%s", No size? %s', entry['title'], size_text, ) entries.add(entry) return sorted(entries, reverse=True, key=lambda x: x.get('torrent_availability'))
def create_entries(self, soup, imdb_id=None): entries = [] links = soup.findAll( 'a', attrs={'href': re.compile(r'download\.php\?torrent=\d+')}) rows = [l.find_parent('tr') for l in links] for row in rows: entry = Entry() entry['title'] = row.find('a', attrs={ 'href': re.compile(r'detail\.php\?id') }).text dl_href = row.find('a', attrs={ 'href': re.compile(r'download\.php\?torrent=\d+') }).get('href') entry['url'] = 'http://piratethenet.org' + dl_href entry['torrent_seeds'] = int( row.find(title='Number of Seeders').text) entry['torrent_leeches'] = int( row.find(title='Number of Leechers').text) entry['torrent_availability'] = torrent_availability( entry['torrent_seeds'], entry['torrent_leeches']) entry['content_size'] = parse_filesize(str( row.find(title='Torrent size').text), si=False) if imdb_id: entry['imdb_id'] = imdb_id entries.append(entry) return entries
def entries_from_search(self, name, url=None): """Parses torrent download url from search results""" name = normalize_unicode(name) if not url: url = 'http://www.newtorrents.info/search/%s' % quote( name.encode('utf-8'), safe=b':/~?=&%' ) log.debug('search url: %s' % url) html = requests.get(url).text # fix </SCR'+'IPT> so that BS does not crash # TODO: should use beautifulsoup massage html = re.sub(r'(</SCR.*?)...(.*?IPT>)', r'\1\2', html) soup = get_soup(html) # saving torrents in dict torrents = [] for link in soup.find_all('a', attrs={'href': re.compile('down.php')}): torrent_url = 'http://www.newtorrents.info%s' % link.get('href') release_name = link.parent.next.get('title') # quick dirty hack seed = link.find_next('td', attrs={'class': re.compile('s')}).renderContents() if seed == 'n/a': seed = 0 else: try: seed = int(seed) except ValueError: log.warning( 'Error converting seed value (%s) from newtorrents to integer.' % seed ) seed = 0 # TODO: also parse content_size and peers from results torrents.append( Entry( title=release_name, url=torrent_url, torrent_seeds=seed, torrent_availability=torrent_availability(seed, 0), ) ) # sort with seed number Reverse order torrents.sort(reverse=True, key=lambda x: x.get('torrent_availability', 0)) # choose the torrent if not torrents: dashindex = name.rfind('-') if dashindex != -1: return self.entries_from_search(name[:dashindex]) else: return torrents else: if len(torrents) == 1: log.debug('found only one matching search result.') else: log.debug( 'search result contains multiple matches, sorted %s by most seeders' % torrents ) return torrents
def search(self, task, entry, config=None): """ Search for name from piratebay. """ if not isinstance(config, dict): config = {} self.set_urls(config.get('url', URL)) sort = SORT.get(config.get('sort_by', 'seeds')) if config.get('sort_reverse'): sort += 1 if isinstance(config.get('category'), int): category = config['category'] else: category = CATEGORIES.get(config.get('category', 'all')) filter_url = '/0/%d/%d' % (sort, category) entries = set() for search_string in entry.get('search_strings', [entry['title']]): query = normalize_unicode(search_string) # TPB search doesn't like dashes or quotes query = query.replace('-', ' ').replace("'", " ") # urllib.quote will crash if the unicode string has non ascii characters, so encode in utf-8 beforehand url = '%s/search/%s%s' % (self.url, quote(query.encode('utf-8')), filter_url) log.debug('Using %s as piratebay search url' % url) page = task.requests.get(url).content soup = get_soup(page) for link in soup.find_all('a', attrs={'class': 'detLink'}): entry = Entry() entry['title'] = self.extract_title(link) if not entry['title']: log.error('Malformed search result. No title or url found. Skipping.') continue href = link.get('href') if href.startswith('/'): # relative link? href = self.url + href entry['url'] = href tds = link.parent.parent.parent.find_all('td') entry['torrent_seeds'] = int(tds[-2].contents[0]) entry['torrent_leeches'] = int(tds[-1].contents[0]) entry['torrent_availability'] = torrent_availability( entry['torrent_seeds'], entry['torrent_leeches'] ) # Parse content_size size_text = link.find_next(attrs={'class': 'detDesc'}).get_text() if size_text: size = re.search(r'Size (\d+(\.\d+)?\xa0(?:[PTGMK])?i?B)', size_text) if size: entry['content_size'] = parse_filesize(size.group(1)) else: log.error( 'Malformed search result? Title: "%s", No size? %s', entry['title'], size_text, ) entries.add(entry) return sorted(entries, reverse=True, key=lambda x: x.get('search_sort'))
def create_entries(self, soup, imdb_id=None): entries = [] links = soup.findAll('a', attrs={'href': re.compile(r'download\.php\?torrent=\d+')}) rows = [l.find_parent('tr') for l in links] for row in rows: entry = Entry() entry['title'] = row.find('a', attrs={'href': re.compile(r'detail\.php\?id')}).text dl_href = row.find('a', attrs={'href': re.compile(r'download\.php\?torrent=\d+')}).get( 'href' ) entry['url'] = 'http://piratethenet.org' + dl_href entry['torrent_seeds'] = int(row.find(title='Number of Seeders').text) entry['torrent_leeches'] = int(row.find(title='Number of Leechers').text) entry['torrent_availability'] = torrent_availability( entry['torrent_seeds'], entry['torrent_leeches'] ) entry['content_size'] = parse_filesize( str(row.find(title='Torrent size').text), si=False ) if imdb_id: entry['imdb_id'] = imdb_id entries.append(entry) return entries
def search(self, task, entry, config=None): config = self.process_config(config) feed = REPUTATIONS[config['reputation']] entries = set() for search_string in entry.get('search_strings', [entry['title']]): query = normalize_unicode(search_string.strip() + config.get('extra_terms', '')) for domain in ['is', 'pl']: # urllib.quote will crash if the unicode string has non ascii characters, so encode in utf-8 beforehand url = 'http://torrentz2.%s/%s?f=%s' % (domain, feed, quote(query.encode('utf-8'))) logger.debug('requesting: {}', url) try: r = task.requests.get(url) break except requests.ConnectionError as err: # The different domains all resolve to the same ip, so only try more if it was a dns error logger.warning('torrentz.{} connection failed. Error: {}', domain, err) continue except requests.RequestException as err: raise plugin.PluginError('Error getting torrentz search results: %s' % err) else: raise plugin.PluginError('Error getting torrentz search results') if not r.content.strip(): raise plugin.PluginError( 'No data from %s. Maybe torrentz is blocking the FlexGet User-Agent' % url ) rss = feedparser.parse(r.content) if rss.get('bozo_exception'): raise plugin.PluginError('Got bozo_exception (bad rss feed)') for item in rss.entries: m = re.search( r'Size: ([\d]+) Mb Seeds: ([,\d]+) Peers: ([,\d]+) Hash: ([a-f0-9]+)', item.description, re.IGNORECASE, ) if not m: logger.debug('regexp did not find seeds / peer data') continue entry = Entry() entry['title'] = item.title entry['url'] = item.link entry['content_size'] = int(m.group(1)) entry['torrent_seeds'] = int(m.group(2).replace(',', '')) entry['torrent_leeches'] = int(m.group(3).replace(',', '')) entry['torrent_info_hash'] = m.group(4).upper() entry['torrent_availability'] = torrent_availability( entry['torrent_seeds'], entry['torrent_leeches'] ) entries.add(entry) logger.debug('Search got {} results', len(entries)) return entries
def search(self, task, entry, config=None): config = self.process_config(config) feed = REPUTATIONS[config['reputation']] entries = set() for search_string in entry.get('search_strings', [entry['title']]): query = normalize_unicode(search_string + config.get('extra_terms', '')) for domain in ['eu', 'is']: # urllib.quote will crash if the unicode string has non ascii characters, so encode in utf-8 beforehand url = 'http://torrentz2.%s/%s?f=%s' % (domain, feed, quote(query.encode('utf-8'))) log.debug('requesting: %s' % url) try: r = task.requests.get(url) break except requests.ConnectionError as err: # The different domains all resolve to the same ip, so only try more if it was a dns error log.warning('torrentz.%s connection failed. Error: %s' % (domain, err)) continue except requests.RequestException as err: raise plugin.PluginError('Error getting torrentz search results: %s' % err) else: raise plugin.PluginError('Error getting torrentz search results') if not r.content.strip(): raise plugin.PluginError( 'No data from %s. Maybe torrentz is blocking the FlexGet User-Agent' % url ) rss = feedparser.parse(r.content) if rss.get('bozo_exception'): raise plugin.PluginError('Got bozo_exception (bad rss feed)') for item in rss.entries: m = re.search( r'Size: ([\d]+) Mb Seeds: ([,\d]+) Peers: ([,\d]+) Hash: ([a-f0-9]+)', item.description, re.IGNORECASE, ) if not m: log.debug('regexp did not find seeds / peer data') continue entry = Entry() entry['title'] = item.title entry['url'] = item.link entry['content_size'] = int(m.group(1)) entry['torrent_seeds'] = int(m.group(2).replace(',', '')) entry['torrent_leeches'] = int(m.group(3).replace(',', '')) entry['torrent_info_hash'] = m.group(4).upper() entry['torrent_availability'] = torrent_availability( entry['torrent_seeds'], entry['torrent_leeches'] ) entries.add(entry) log.debug('Search got %d results' % len(entries)) return entries
def search(self, task, entry, config): """Search for entries on HeBits""" account_info = self._fetch_account_info(self.user_profile_url, config) params = { 'action': 'browse', 'group_results': 0, } cookies = {"userid": f"{config['userid']}", "session": f"{config['session']}"} if 'category' in config: cat = HeBitsCategory[config['category']].value params[f'filter_cat[{cat}]'] = 1 entries = set() params['order_by'] = HeBitsSort[config['order_by']].value params['order_way'] = 'desc' if config['order_desc'] else 'asc' for search_string in entry.get( 'search_strings', [entry['title'], entry.get('original_title'), entry.get('imdb_id')] ): params['searchstr'] = search_string logger.debug('Using search params: {}', params) try: page = requests.get(self.search_url, cookies=cookies, params=params) page.raise_for_status() search_results_response = page.json() except RequestException as e: logger.error('HeBits request failed: {}', e) continue if search_results_response['status'] != 'success': logger.error('HeBits request failed: server error') continue search_results = search_results_response['response']['results'] for result in search_results: torrent_id = result['torrents'][0]['torrentId'] seeders = result['torrents'][0]['seeders'] leechers = result['torrents'][0]['leechers'] size = result['torrents'][0]['size'] / 2**20 title = result['torrents'][0]['release'] entry = Entry( torrent_seeds=seeders, torrent_leeches=leechers, torrent_availability=torrent_availability(seeders, leechers), content_size=size, title=title, torrent_freeleech=result['torrents'][0]['isFreeleech'], torrent_triple_up=result['torrents'][0]['isUploadX3'], torrent_double_up=result['torrents'][0]['isUploadX2'], url=f"{self.download_url}?action=download&id={torrent_id}&authkey={account_info['authkey']}&torrent_pass={account_info['passkey']}", ) entries.add(entry) return entries
def search(self, task, entry, config=None): entries = set() search_strings = [ normalize_unicode(s) for s in entry.get('search_strings', [entry['title']]) ] for search_string in search_strings: url = 'https://yts.am/api/v2/list_movies.json?query_term=%s' % ( quote(search_string.encode('utf-8'))) log.debug('requesting: %s' % url) try: result = requests.get(url) try: data = result.json() except ValueError: log.debug('Could not decode json from response: %s', result.text) raise plugin.PluginError('Error getting result from yts.') except requests.RequestException as e: raise plugin.PluginError( 'Could not retrieve query from yts (%s)' % e.args[0]) if not data['status'] == 'ok': raise plugin.PluginError('failed to query YTS') try: if data['data']['movie_count'] > 0: for item in data['data']['movies']: for torrent in item['torrents']: entry = Entry() entry['title'] = item['title'] entry['year'] = item['year'] entry['url'] = torrent['url'] entry['content_size'] = parse_filesize( str(torrent['size_bytes']) + "b") entry['torrent_seeds'] = torrent['seeds'] entry['torrent_leeches'] = torrent['peers'] entry['torrent_info_hash'] = torrent['hash'] entry[ 'torrent_availability'] = torrent_availability( entry['torrent_seeds'], entry['torrent_leeches']) entry['quality'] = torrent['quality'] entry['imdb_id'] = item['imdb_code'] if entry.isvalid(): entries.add(entry) except Exception: log.debug('invalid return structure from YTS') log.debug('Search got %d results' % len(entries)) return entries
def json_to_entry(self, json_result: dict) -> Entry: entry = Entry() entry['title'] = json_result['name'] entry['torrent_seeds'] = int(json_result['seeders']) entry['torrent_leeches'] = int(json_result['leechers']) entry['torrent_timestamp'] = int(json_result['added']) # custom field for sorting by date entry['torrent_availability'] = torrent_availability( entry['torrent_seeds'], entry['torrent_leeches'] ) entry['content_size'] = int(round(int(json_result['size']) / (1024 * 1024))) entry['torrent_info_hash'] = json_result['info_hash'] entry['url'] = self.info_hash_to_magnet(json_result['info_hash'], json_result['name']) return entry
def extract_entry_from_soup(self, soup): table = soup.find('div', {'id': 'main_table'}) if table is None: raise PluginError( 'Could not fetch results table from Fuzer, aborting') log.trace('fuzer results table: %s', table) table = table.find('table', {'class': 'table_info'}) if len(table.find_all('tr')) == 1: log.debug('No search results were returned from Fuzer, continuing') return [] entries = [] for tr in table.find_all("tr"): if not tr.get('class') or 'colhead_dark' in tr.get('class'): continue name = tr.find('div', {'class': 'main_title'}).find('a').text torrent_name = re.search( '\\n(.*)', tr.find('div', { 'style': 'float: right;' }).find('a')['title']).group(1) attachment_link = tr.find('div', { 'style': 'float: right;' }).find('a')['href'] attachment_id = re.search(r'attachmentid=(\d+)', attachment_link).group(1) raw_size = tr.find_all('td', {'class': 'inline_info'})[0].text.strip() seeders = int(tr.find_all('td', {'class': 'inline_info'})[2].text) leechers = int(tr.find_all('td', {'class': 'inline_info'})[3].text) e = Entry() e['title'] = name final_url = 'https://www.fuzer.me/rss/torrent.php/{}/{}/{}/{}'.format( attachment_id, self.user_id, self.rss_key, torrent_name) log.debug('RSS-ified download link: %s', final_url) e['url'] = final_url e['torrent_seeds'] = seeders e['torrent_leeches'] = leechers e['torrent_availibility'] = torrent_availability( e['torrent_seeds'], e['torrent_leeches']) size = re.search(r'(\d+(?:[.,]\d+)*)\s?([KMGTP]B)', raw_size) e['content_size'] = parse_filesize(size.group(0)) entries.append(e) return entries
def search(self, task, entry, config=None): entries = set() search_strings = [ normalize_unicode(s) for s in entry.get('search_strings', [entry['title']]) ] for search_string in search_strings: url = 'https://yts.am/api/v2/list_movies.json?query_term=%s' % ( urllib.quote(search_string.encode('utf-8')) ) log.debug('requesting: %s' % url) try: result = requests.get(url) try: data = result.json() except ValueError: log.debug('Could not decode json from response: %s', result.text) raise plugin.PluginError('Error getting result from yts.') except requests.RequestException as e: raise plugin.PluginError('Could not retrieve query from yts (%s)' % e.args[0]) if not data['status'] == 'ok': raise plugin.PluginError('failed to query YTS') try: if data['data']['movie_count'] > 0: for item in data['data']['movies']: for torrent in item['torrents']: entry = Entry() entry['title'] = item['title'] entry['year'] = item['year'] entry['url'] = torrent['url'] entry['content_size'] = parse_filesize( str(torrent['size_bytes']) + "b" ) entry['torrent_seeds'] = torrent['seeds'] entry['torrent_leeches'] = torrent['peers'] entry['torrent_info_hash'] = torrent['hash'] entry['torrent_availability'] = torrent_availability( entry['torrent_seeds'], entry['torrent_leeches'] ) entry['quality'] = torrent['quality'] entry['imdb_id'] = item['imdb_code'] if entry.isvalid(): entries.add(entry) except Exception: log.debug('invalid return structure from YTS') log.debug('Search got %d results' % len(entries)) return entries
def _parse_torznab_attrs(self, entry, attrs): """Parse the torznab::attr values from the response https://github.com/Sonarr/Sonarr/wiki/Implementing-a-Torznab-indexer#torznab-results """ dictionary = { 'episode': {'name': 'series_episode', 'type': int}, 'imdbid': {'name': 'imdb_id', 'type': str}, 'infohash': {'name': 'torrent_info_hash', 'type': str}, 'leechers': {'name': 'torrent_leeches', 'type': int}, 'rageid': {'name': 'tvrage_id', 'type': int}, 'season': {'name': 'series_season', 'type': int}, 'seeders': {'name': 'torrent_seeds', 'type': int}, 'title': {'name': 'series_name', 'type': str}, 'tmdbid': {'name': 'tmdb_id', 'type': int}, 'traktid': {'name': 'trakt_id', 'type': int}, 'tvdbid': {'name': 'tvdb_id', 'type': int}, 'tvmazeid': {'name': 'tvmaze_series_id', 'type': int}, 'tvrageid': {'name': 'tvrage_id', 'type': int}, } misc = {} for attr in attrs: name = attr.get('name') if name in dictionary.keys(): entry[dictionary[name]['name']] = dictionary[name]['type'](attr.get('value')) elif name == 'peers': misc['peers'] = int(attr.get('value')) elif name == 'imdb': misc['imdb'] = str(attr.get('value')) elif name == 'size': misc['size'] = int(attr.get('value')) if 'imdb_id' not in entry.keys() and 'imdb' in misc.keys(): entry['imdb_id'] = 'tt{}'.format(misc['imdb']) if 'peers' in misc.keys(): if 'torrent_leeches' not in entry.keys() and 'torrent_seeds' in entry.keys(): entry['torrent_leeches'] = misc['peers'] - entry['torrent_seeds'] if 'torrent_leeches' in entry.keys() and 'torrent_seeds' not in entry.keys(): entry['torrent_seeds'] = misc['peers'] - entry['torrent_leeches'] if 'content_size' not in entry.keys() and 'size' in misc.keys(): entry['content_size'] = misc['size'] // (2 ** 20) if 'torrent_seeds' in entry.keys() and 'torrent_leeches' in entry.keys(): entry['torrent_availability'] = torrent_availability( entry['torrent_seeds'], entry['torrent_leeches'] )
def _parse_torznab_attrs(self, entry, attrs): """Parse the torznab::attr values from the response https://github.com/Sonarr/Sonarr/wiki/Implementing-a-Torznab-indexer#torznab-results """ dictionary = { 'episode': {'name': 'series_episode', 'type': int}, 'imdbid': {'name': 'imdb_id', 'type': str}, 'infohash': {'name': 'torrent_info_hash', 'type': str}, 'leechers': {'name': 'torrent_leeches', 'type': int}, 'rageid': {'name': 'tvrage_id', 'type': int}, 'season': {'name': 'series_season', 'type': int}, 'seeders': {'name': 'torrent_seeds', 'type': int}, 'title': {'name': 'series_name', 'type': str}, 'tmdbid': {'name': 'tmdb_id', 'type': int}, 'traktid': {'name': 'trakt_id', 'type': int}, 'tvdbid': {'name': 'tvdb_id', 'type': int}, 'tvmazeid': {'name': 'tvmaze_series_id', 'type': int}, 'tvrageid': {'name': 'tvrage_id', 'type': int} } misc = {} for attr in attrs: name = attr.get('name') if name in dictionary.keys(): entry[dictionary[name]['name']] = dictionary[name]['type'](attr.get('value')) elif name == 'peers': misc['peers'] = int(attr.get('value')) elif name == 'imdb': misc['imdb'] = str(attr.get('value')) elif name == 'size': misc['size'] = int(attr.get('value')) if 'imdb_id' not in entry.keys() and 'imdb' in misc.keys(): entry['imdb_id'] = 'tt{}'.format(misc['imdb']) if 'peers' in misc.keys(): if 'torrent_leeches' not in entry.keys() and 'torrent_seeds' in entry.keys(): entry['torrent_leeches'] = misc['peers'] - entry['torrent_seeds'] if 'torrent_leeches' in entry.keys() and 'torrent_seeds' not in entry.keys(): entry['torrent_seeds'] = misc['peers'] - entry['torrent_leeches'] if 'content_size' not in entry.keys() and 'size' in misc.keys(): entry['content_size'] = old_div(misc['size'], 2 ** 20) if 'torrent_seeds' in entry.keys() and 'torrent_leeches' in entry.keys(): entry['torrent_availability'] = torrent_availability( entry['torrent_seeds'], entry['torrent_leeches'] )
def json_to_entry(self, json_result: dict) -> Entry: entry = Entry() entry['title'] = json_result['title'] entry['torrent_seeds'] = int(json_result['swarm']['seeders']) entry['torrent_leeches'] = int(json_result['swarm']['leechers']) entry['torrent_timestamp'] = int( time.mktime( datetime.strptime(json_result['imported'], '%Y-%m-%dT%H:%M:%S.%fZ').timetuple())) entry['torrent_availability'] = torrent_availability( entry['torrent_seeds'], entry['torrent_leeches']) entry['content_size'] = int( round(int(json_result['size']) / (1024 * 1024))) # content_size is in MiB entry['torrent_info_hash'] = json_result['infohash'] entry['url'] = json_result['magnet'] return entry
def extract_entry_from_soup(self, soup): table = soup.find('div', {'id': 'main_table'}) if table is None: raise PluginError('Could not fetch results table from Fuzer, aborting') log.trace('fuzer results table: %s', table) table = table.find('table', {'class': 'table_info'}) if len(table.find_all('tr')) == 1: log.debug('No search results were returned from Fuzer, continuing') return [] entries = [] for tr in table.find_all("tr"): if not tr.get('class') or 'colhead_dark' in tr.get('class'): continue name = tr.find('div', {'class': 'main_title'}).find('a').text torrent_name = re.search( '\\n(.*)', tr.find('div', {'style': 'float: right;'}).find('a')['title'] ).group(1) attachment_link = tr.find('div', {'style': 'float: right;'}).find('a')['href'] attachment_id = re.search(r'attachmentid=(\d+)', attachment_link).group(1) raw_size = tr.find_all('td', {'class': 'inline_info'})[0].text.strip() seeders = int(tr.find_all('td', {'class': 'inline_info'})[2].text) leechers = int(tr.find_all('td', {'class': 'inline_info'})[3].text) e = Entry() e['title'] = name final_url = 'https://www.fuzer.me/rss/torrent.php/{}/{}/{}/{}'.format( attachment_id, self.user_id, self.rss_key, torrent_name ) log.debug('RSS-ified download link: %s', final_url) e['url'] = final_url e['torrent_seeds'] = seeders e['torrent_leeches'] = leechers e['torrent_availability'] = torrent_availability( e['torrent_seeds'], e['torrent_leeches'] ) size = re.search(r'(\d+(?:[.,]\d+)*)\s?([KMGTP]B)', raw_size) e['content_size'] = parse_filesize(size.group(0)) entries.append(e) return entries
def search(self, task, entry, config): if not isinstance(config, dict): config = {'category': config} config.setdefault('category', 'anime eng') config.setdefault('filter', 'all') entries = set() for search_string in entry.get('search_strings', [entry['title']]): name = normalize_unicode(search_string) url = 'https://www.nyaa.si/?page=rss&q=%s&c=%s&f=%s' % ( quote(name.encode('utf-8')), CATEGORIES[config['category']], FILTERS.index(config['filter']), ) log.debug('requesting: %s' % url) rss = feedparser.parse(url) status = rss.get('status', False) if status != 200: log.debug('Search result not 200 (OK), received %s' % status) if status >= 400: continue ex = rss.get('bozo_exception', False) if ex: log.error('Got bozo_exception (bad feed) on %s' % url) continue for item in rss.entries: entry = Entry() entry['title'] = item.title entry['url'] = item.link entry['torrent_seeds'] = int(item.nyaa_seeders) entry['torrent_leeches'] = int(item.nyaa_leechers) entry['torrent_info_hash'] = item.nyaa_infohash entry['torrent_availability'] = torrent_availability( entry['torrent_seeds'], entry['torrent_leeches'] ) if item.nyaa_size: entry['content_size'] = parse_filesize(item.nyaa_size) entries.add(entry) return entries
def search(self, task, entry, config): if not isinstance(config, dict): config = {'category': config} config.setdefault('category', 'anime eng') config.setdefault('filter', 'all') entries = set() for search_string in entry.get('search_strings', [entry['title']]): name = normalize_unicode(search_string) url = 'https://www.nyaa.si/?page=rss&q=%s&c=%s&f=%s' % ( quote(name.encode('utf-8')), CATEGORIES[config['category']], FILTERS.index(config['filter']), ) log.debug('requesting: %s' % url) rss = feedparser.parse(url) status = rss.get('status', False) if status != 200: log.debug('Search result not 200 (OK), received %s' % status) if status >= 400: continue ex = rss.get('bozo_exception', False) if ex: log.error('Got bozo_exception (bad feed) on %s' % url) continue for item in rss.entries: entry = Entry() entry['title'] = item.title entry['url'] = item.link entry['torrent_seeds'] = int(item.nyaa_seeders) entry['torrent_leeches'] = int(item.nyaa_leechers) entry['torrent_info_hash'] = item.nyaa_infohash entry['torrent_availability'] = torrent_availability( entry['torrent_seeds'], entry['torrent_leeches']) if item.nyaa_size: entry['content_size'] = parse_filesize(item.nyaa_size) entries.add(entry) return entries
def search(self, task, entry, config=None): """ Search for name from iptorrents """ categories = config.get('category', 'All') # Make sure categories is a list if not isinstance(categories, list): categories = [categories] # If there are any text categories, turn them into their id number categories = [c if isinstance(c, int) else CATEGORIES[c] for c in categories] filter_url = '&'.join((str(c) + '=') for c in categories) entries = set() for search_string in entry.get('search_strings', [entry['title']]): query = normalize_unicode(search_string) query = quote_plus(query.encode('utf8')) url = "{base_url}/t?{filter}&q={query}&qf=".format( base_url=BASE_URL, filter=filter_url, query=query ) log.debug('searching with url: %s' % url) req = requests.get( url, cookies={'uid': str(config['uid']), 'pass': config['password']} ) if '/u/' + str(config['uid']) not in req.text: raise plugin.PluginError("Invalid cookies (user not logged in)...") soup = get_soup(req.content, parser="html.parser") torrents = soup.find('table', {'id': 'torrents'}) results = torrents.findAll('tr') for torrent in results: if torrent.th and 'ac' in torrent.th.get('class'): # Header column continue if torrent.find('td', {'colspan': '99'}): log.debug('No results found for search %s', search_string) break entry = Entry() link = torrent.find('a', href=re.compile('download'))['href'] entry['url'] = "{base}{link}?torrent_pass={key}".format( base=BASE_URL, link=link, key=config.get('rss_key') ) entry['title'] = torrent.find('a', href=re.compile('details')).text seeders = torrent.findNext('td', {'class': 'ac t_seeders'}).text leechers = torrent.findNext('td', {'class': 'ac t_leechers'}).text entry['torrent_seeds'] = int(seeders) entry['torrent_leeches'] = int(leechers) entry['torrent_availability'] = torrent_availability( entry['torrent_seeds'], entry['torrent_leeches'] ) size = torrent.findNext(text=re.compile(r'^([\.\d]+) ([GMK]?)B$')) size = re.search(r'^([\.\d]+) ([GMK]?)B$', size) entry['content_size'] = parse_filesize(size.group(0)) log.debug('Found entry %s', entry) entries.add(entry) return entries
def search(self, task, entry, config=None): """ Search for name from torrentleech. """ request_headers = {'User-Agent': 'curl/7.54.0'} rss_key = config['rss_key'] # build the form request: data = {'username': config['username'], 'password': config['password']} # POST the login form: try: login = task.requests.post( 'https://www.torrentleech.org/user/account/login/', data=data, headers=request_headers, allow_redirects=True, ) except RequestException as e: raise PluginError('Could not connect to torrentleech: %s' % str(e)) if login.url.endswith('/user/account/login/'): raise PluginError( 'Could not login to torrentleech, faulty credentials?') if not isinstance(config, dict): config = {} # sort = SORT.get(config.get('sort_by', 'seeds')) # if config.get('sort_reverse'): # sort += 1 categories = config.get('category', 'all') # Make sure categories is a list if not isinstance(categories, list): categories = [categories] # If there are any text categories, turn them into their id number categories = [ c if isinstance(c, int) else CATEGORIES[c] for c in categories ] filter_url = '/categories/{}'.format(','.join( str(c) for c in categories)) entries = set() for search_string in entry.get('search_strings', [entry['title']]): query = normalize_unicode(search_string).replace(":", "") # urllib.quote will crash if the unicode string has non ascii characters, # so encode in utf-8 beforehand url = ('https://www.torrentleech.org/torrents/browse/list/query/' + quote(query.encode('utf-8')) + filter_url) log.debug('Using %s as torrentleech search url', url) results = task.requests.get(url, headers=request_headers, cookies=login.cookies).json() for torrent in results['torrentList']: entry = Entry() entry['download_headers'] = request_headers entry['title'] = torrent['name'] # construct download URL torrent_url = 'https://www.torrentleech.org/rss/download/{}/{}/{}'.format( torrent['fid'], rss_key, torrent['filename']) log.debug('RSS-ified download link: %s', torrent_url) entry['url'] = torrent_url # seeders/leechers entry['torrent_seeds'] = torrent['seeders'] entry['torrent_leeches'] = torrent['leechers'] entry['torrent_availability'] = torrent_availability( entry['torrent_seeds'], entry['torrent_leeches']) entry['content_size'] = parse_filesize( str(torrent['size']) + ' b') entries.add(entry) return sorted(entries, reverse=True, key=lambda x: x.get('torrent_availability'))
def search(self, task, entry, config): """ Search for entries on 1337x """ if not isinstance(config, dict): config = {} order_by = '' sort_order = '' if isinstance(config.get('order_by'), str): if config['order_by'] != 'leechers': order_by = '/{0}/desc'.format(config['order_by']) sort_order = 'sort-' entries = set() for search_string in entry.get('search_strings', [entry['title']]): query = '{0}search/{1}{2}/1/'.format( sort_order, quote(search_string.encode('utf8')), order_by ) log.debug( 'Using search params: %s; ordering by: %s', search_string, order_by or 'default' ) try: page = task.requests.get(self.base_url + query) log.debug('requesting: %s', page.url) except RequestException as e: log.error('1337x request failed: %s', e) continue soup = get_soup(page.content) if soup.find('div', attrs={'class': 'table-list-wrap'}) is not None: for link in soup.find('div', attrs={'class': 'table-list-wrap'}).findAll( 'a', href=re.compile('^/torrent/') ): li = link.parent.parent title = str(link.text).replace('...', '') info_url = self.base_url + str(link.get('href'))[1:] seeds = int(li.find('td', class_='seeds').string) leeches = int(li.find('td', class_='leeches').string) size = str(li.find('td', class_='coll-4').contents[0]) size = parse_filesize(size) e = Entry() e['url'] = info_url e['title'] = title e['torrent_seeds'] = seeds e['torrent_leeches'] = leeches e['torrent_availability'] = torrent_availability( e['torrent_seeds'], e['torrent_leeches'] ) e['content_size'] = size entries.add(e) return entries
def search(self, task, entry, config=None): """ Search for name from torrentday. """ categories = config.get('category', 'all') # Make sure categories is a list if not isinstance(categories, list): categories = [categories] # If there are any text categories, turn them into their id number categories = [ c if isinstance(c, int) else CATEGORIES[c] for c in categories ] params = { 'cata': 'yes', 'c{}'.format(','.join(str(c) for c in categories)): 1, 'clear-new': 1, } entries = set() for search_string in entry.get('search_strings', [entry['title']]): url = 'https://www.torrentday.com/t' params['q'] = normalize_unicode(search_string).replace(':', '') cookies = { 'uid': config['uid'], 'pass': config['passkey'], '__cfduid': config['cfduid'], } try: page = requests.get(url, params=params, cookies=cookies).content except RequestException as e: raise PluginError( 'Could not connect to torrentday: {}'.format(e)) # the following should avoid table being None due to a malformed # html in td search results soup = get_soup(page).contents[1].contents[1].next.next.nextSibling table = soup.find('table', {'id': 'torrentTable'}) if table is None: raise PluginError( 'Search returned by torrentday appears to be empty or malformed.' ) # the first row is the header so skip it for tr in table.find_all('tr')[1:]: entry = Entry() # find the torrent names td = tr.find('td', {'class': 'torrentNameInfo'}) if not td: log.warning('Could not find entry torrentNameInfo for %s.', search_string) continue title = td.find('a') if not title: log.warning('Could not determine title for %s.', search_string) continue entry['title'] = title.contents[0] log.debug('title: %s', title.contents[0]) # find download link torrent_url = tr.find('td', {'class': 'ac'}) if not torrent_url: log.warning('Could not determine download link for %s.', search_string) continue torrent_url = torrent_url.find('a').get('href') # construct download URL torrent_url = ('https://www.torrentday.com/' + torrent_url + '?torrent_pass='******'rss_key']) log.debug('RSS-ified download link: %s', torrent_url) entry['url'] = torrent_url # us tr object for seeders/leechers seeders = tr.find('td', {'class': 'ac seedersInfo'}) leechers = tr.find('td', {'class': 'ac leechersInfo'}) entry['torrent_seeds'] = int(seeders.contents[0].replace( ',', '')) entry['torrent_leeches'] = int(leechers.contents[0].replace( ',', '')) entry['torrent_availability'] = torrent_availability( entry['torrent_seeds'], entry['torrent_leeches']) # use tr object for size size = tr.find( 'td', text=re.compile(r'([\.\d]+) ([TGMKk]?)B')).contents[0] size = re.search(r'([\.\d]+) ([TGMKk]?)B', str(size)) entry['content_size'] = parse_filesize(size.group(0)) entries.add(entry) return sorted(entries, reverse=True, key=lambda x: x.get('torrent_availability'))
def entries_from_search(self, name, url=None): """Parses torrent download url from search results""" name = normalize_unicode(name) if not url: url = 'http://www.newtorrents.info/search/%s' % quote( name.encode('utf-8'), safe=b':/~?=&%') log.debug('search url: %s' % url) html = requests.get(url).text # fix </SCR'+'IPT> so that BS does not crash # TODO: should use beautifulsoup massage html = re.sub(r'(</SCR.*?)...(.*?IPT>)', r'\1\2', html) soup = get_soup(html) # saving torrents in dict torrents = [] for link in soup.find_all('a', attrs={'href': re.compile('down.php')}): torrent_url = 'http://www.newtorrents.info%s' % link.get('href') release_name = link.parent.next.get('title') # quick dirty hack seed = link.find_next('td', attrs={ 'class': re.compile('s') }).renderContents() if seed == 'n/a': seed = 0 else: try: seed = int(seed) except ValueError: log.warning( 'Error converting seed value (%s) from newtorrents to integer.' % seed) seed = 0 # TODO: also parse content_size and peers from results torrents.append( Entry( title=release_name, url=torrent_url, torrent_seeds=seed, torrent_availability=torrent_availability(seed, 0), )) # sort with seed number Reverse order torrents.sort(reverse=True, key=lambda x: x.get('torrent_availability', 0)) # choose the torrent if not torrents: dashindex = name.rfind('-') if dashindex != -1: return self.entries_from_search(name[:dashindex]) else: return torrents else: if len(torrents) == 1: log.debug('found only one matching search result.') else: log.debug( 'search result contains multiple matches, sorted %s by most seeders' % torrents) return torrents
def search(self, task, entry, config=None): """ Search for name from torrentday. """ categories = config.get('category', 'all') # Make sure categories is a list if not isinstance(categories, list): categories = [categories] # If there are any text categories, turn them into their id number categories = [c if isinstance(c, int) else CATEGORIES[c] for c in categories] params = { 'cata': 'yes', 'c{}'.format(','.join(str(c) for c in categories)): 1, 'clear-new': 1, } entries = set() for search_string in entry.get('search_strings', [entry['title']]): url = 'https://www.torrentday.com/t' params['q'] = normalize_unicode(search_string).replace(':', '') cookies = { 'uid': config['uid'], 'pass': config['passkey'], '__cfduid': config['cfduid'], } try: page = requests.get(url, params=params, cookies=cookies).content except RequestException as e: raise PluginError('Could not connect to torrentday: {}'.format(e)) # the following should avoid table being None due to a malformed # html in td search results soup = get_soup(page).contents[1].contents[1].next.next.nextSibling table = soup.find('table', {'id': 'torrentTable'}) if table is None: raise PluginError( 'Search returned by torrentday appears to be empty or malformed.' ) # the first row is the header so skip it for tr in table.find_all('tr')[1:]: entry = Entry() # find the torrent names td = tr.find('td', {'class': 'torrentNameInfo'}) if not td: log.warning('Could not find entry torrentNameInfo for %s.', search_string) continue title = td.find('a') if not title: log.warning('Could not determine title for %s.', search_string) continue entry['title'] = title.contents[0] log.debug('title: %s', title.contents[0]) # find download link torrent_url = tr.find('td', {'class': 'ac'}) if not torrent_url: log.warning('Could not determine download link for %s.', search_string) continue torrent_url = torrent_url.find('a').get('href') # construct download URL torrent_url = ( 'https://www.torrentday.com/' + torrent_url + '?torrent_pass='******'rss_key'] ) log.debug('RSS-ified download link: %s', torrent_url) entry['url'] = torrent_url # us tr object for seeders/leechers seeders = tr.find('td', {'class': 'ac seedersInfo'}) leechers = tr.find('td', {'class': 'ac leechersInfo'}) entry['torrent_seeds'] = int(seeders.contents[0].replace(',', '')) entry['torrent_leeches'] = int(leechers.contents[0].replace(',', '')) entry['torrent_availability'] = torrent_availability( entry['torrent_seeds'], entry['torrent_leeches'] ) # use tr object for size size = tr.find('td', text=re.compile(r'([\.\d]+) ([TGMKk]?)B')).contents[0] size = re.search(r'([\.\d]+) ([TGMKk]?)B', str(size)) entry['content_size'] = parse_filesize(size.group(0)) entries.add(entry) return sorted(entries, reverse=True, key=lambda x: x.get('torrent_availability'))
def search(self, task, entry, config): task.requests.add_domain_limiter(self.request_limiter) config = self.prepare_config(config) api_key = config['api_key'] searches = entry.get('search_strings', [entry['title']]) if 'series_name' in entry: if entry.get('season_pack_lookup', False): search = {'category': 'Season'} else: search = {'category': 'Episode'} if config.get('origin'): search['origin'] = config['origin'] if 'tvdb_id' in entry: search['tvdb'] = entry['tvdb_id'] elif 'tvrage_id' in entry: search['tvrage'] = entry['tvrage_id'] else: search['series'] = entry['series_name'] if entry.get('season_pack_lookup', False) and 'series_season' in entry: search['name'] = 'Season %s' % entry['series_season'] elif 'series_id' in entry: # BTN wants an ep style identifier even for sequence shows if entry.get('series_id_type') == 'sequence': search['name'] = 'S01E%02d' % entry['series_id'] else: search['name'] = ( entry['series_id'] + '%' ) # added wildcard search for better results. searches = [search] # If searching by series name ending in a parenthetical, try again without it if there are no results. if search.get('series') and search['series'].endswith(')'): match = re.match(r'(.+)\([^\(\)]+\)$', search['series']) if match: searches.append(dict(search, series=match.group(1).strip())) results = set() for search in searches: data = json.dumps({'method': 'getTorrents', 'params': [api_key, search], 'id': 1}) try: r = task.requests.post( 'https://api.broadcasthe.net/', data=data, headers={'Content-type': 'application/json'}, ) except requests.RequestException as e: log.error('Error searching btn: %s' % e) continue try: content = r.json() except ValueError as e: raise plugin.PluginError('Error searching btn. Maybe it\'s down?. %s' % str(e)) if not content or not content['result']: log.debug('No results from btn') if content and content.get('error'): if content['error'].get('code') == -32002: log.error('btn api call limit exceeded, throttling connection rate') self.request_limiter.tokens = -1 else: log.error( 'Error searching btn: %s' % content['error'].get('message', content['error']) ) continue if 'torrents' in content['result']: for item in content['result']['torrents'].values(): entry = Entry() entry['title'] = item['ReleaseName'] if config['append_quality']: entry['title'] += ' '.join( ['', item['Resolution'], item['Source'], item['Codec']] ) entry['url'] = item['DownloadURL'] entry['torrent_seeds'] = int(item['Seeders']) entry['torrent_leeches'] = int(item['Leechers']) entry['torrent_info_hash'] = item['InfoHash'] entry['torrent_availability'] = torrent_availability( entry['torrent_seeds'], entry['torrent_leeches'] ) entry['btn_origin'] = item['Origin'] if item['TvdbID'] and int(item['TvdbID']): entry['tvdb_id'] = int(item['TvdbID']) if item['TvrageID'] and int(item['TvrageID']): entry['tvrage_id'] = int(item['TvrageID']) results.add(entry) # Don't continue searching if this search yielded results break return results
def search(self, task, entry, config): """ Search for entries on Limetorrents """ if not isinstance(config, dict): config = {'category': config} order_by = '' if isinstance(config.get('order_by'), str): if config['order_by'] != 'date': order_by = '{0}/1'.format(config['order_by']) category = 'all' if isinstance(config.get('category'), str): category = '{0}'.format(config['category']) entries = set() for search_string in entry.get('search_strings', [entry['title']]): # No special characters - use dashes instead of %20 cleaned_search_string = clean_symbols(search_string).replace( ' ', '-') query = 'search/{0}/{1}/{2}'.format( category, cleaned_search_string.encode('utf8'), order_by) logger.debug( 'Using search: {}; category: {}; ordering: {}', cleaned_search_string, category, order_by or 'default', ) try: page = task.requests.get(self.base_url + query) logger.debug('requesting: {}', page.url) except RequestException as e: logger.error('Limetorrents request failed: {}', e) continue soup = get_soup(page.content) if soup.find('a', attrs={'class': 'csprite_dl14'}) is not None: for link in soup.findAll('a', attrs={'class': 'csprite_dl14'}): row = link.find_parent('tr') info_url = str(link.get('href')) # Get the title from the URL as it's complete versus the actual Title text which gets cut off title = str(link.next_sibling.get('href')) title = title[:title.rfind('-torrent')].replace('-', ' ') title = title[1:] data = row.findAll('td', attrs={'class': 'tdnormal'}) size = str(data[1].text).replace(',', '') seeds = int( row.find('td', attrs={ 'class': 'tdseed' }).text.replace(',', '')) leeches = int( row.find('td', attrs={ 'class': 'tdleech' }).text.replace(',', '')) size = parse_filesize(size) e = Entry() e['url'] = info_url e['title'] = title e['torrent_seeds'] = seeds e['torrent_leeches'] = leeches e['torrent_availability'] = torrent_availability( e['torrent_seeds'], e['torrent_leeches']) e['content_size'] = size entries.add(e) return entries
def search(self, task, entry, config): """ Search for entries on 1337x """ if not isinstance(config, dict): config = {} order_by = '' sort_order = '' if isinstance(config.get('order_by'), str): if config['order_by'] != 'leechers': order_by = '/{0}/desc'.format(config['order_by']) sort_order = 'sort-' entries = set() for search_string in entry.get('search_strings', [entry['title']]): query = '{0}search/{1}{2}/1/'.format( sort_order, quote(search_string.encode('utf8')), order_by ) log.debug( 'Using search params: %s; ordering by: %s', search_string, order_by or 'default' ) try: page = task.requests.get(self.base_url + query) log.debug('requesting: %s', page.url) except RequestException as e: log.error('1337x request failed: %s', e) continue soup = get_soup(page.content) if soup.find('div', attrs={'class': 'table-list-wrap'}) is not None: for link in soup.find('div', attrs={'class': 'table-list-wrap'}).findAll( 'a', href=re.compile('^/torrent/') ): li = link.parent.parent title = str(link.text).replace('...', '') info_url = self.base_url + str(link.get('href'))[1:] seeds = int(li.find('td', class_='seeds').string) leeches = int(li.find('td', class_='leeches').string) size = str(li.find('td', class_='coll-4').contents[0]) size = parse_filesize(size) e = Entry() e['url'] = info_url e['title'] = title e['torrent_seeds'] = seeds e['torrent_leeches'] = leeches e['torrent_availability'] = torrent_availability( e['torrent_seeds'], e['torrent_leeches'] ) e['content_size'] = size entries.add(e) return entries
def search(self, task, entry, config): """ Search for entries on Limetorrents """ if not isinstance(config, dict): config = {'category': config} order_by = '' if isinstance(config.get('order_by'), str): if config['order_by'] != 'date': order_by = '{0}/1'.format(config['order_by']) category = 'all' if isinstance(config.get('category'), str): category = '{0}'.format(config['category']) entries = set() for search_string in entry.get('search_strings', [entry['title']]): # No special characters - use dashes instead of %20 cleaned_search_string = clean_symbols(search_string).replace(' ', '-') query = 'search/{0}/{1}/{2}'.format( category, cleaned_search_string.encode('utf8'), order_by ) log.debug( 'Using search: %s; category: %s; ordering: %s', cleaned_search_string, category, order_by or 'default', ) try: page = task.requests.get(self.base_url + query) log.debug('requesting: %s', page.url) except RequestException as e: log.error('Limetorrents request failed: %s', e) continue soup = get_soup(page.content) if soup.find('a', attrs={'class': 'csprite_dl14'}) is not None: for link in soup.findAll('a', attrs={'class': 'csprite_dl14'}): row = link.find_parent('tr') info_url = str(link.get('href')) # Get the title from the URL as it's complete versus the actual Title text which gets cut off title = str(link.next_sibling.get('href')) title = title[: title.rfind('-torrent')].replace('-', ' ') title = title[1:] data = row.findAll('td', attrs={'class': 'tdnormal'}) size = str(data[1].text).replace(',', '') seeds = int(row.find('td', attrs={'class': 'tdseed'}).text.replace(',', '')) leeches = int(row.find('td', attrs={'class': 'tdleech'}).text.replace(',', '')) size = parse_filesize(size) e = Entry() e['url'] = info_url e['title'] = title e['torrent_seeds'] = seeds e['torrent_leeches'] = leeches e['torrent_availability'] = torrent_availability( e['torrent_seeds'], e['torrent_leeches'] ) e['content_size'] = size entries.add(e) return entries
def search(self, task, entry, config=None): """ Search for name from iptorrents """ categories = config.get('category', 'All') # Make sure categories is a list if not isinstance(categories, list): categories = [categories] # If there are any text categories, turn them into their id number categories = [ c if isinstance(c, int) else CATEGORIES[c] for c in categories ] category_params = {str(c): '' for c in categories if str(c)} entries = set() for search_string in entry.get('search_strings', [entry['title']]): search_params = { key: value for (key, value) in category_params.items() } query = normalize_unicode(search_string) search_params.update({'q': query, 'qf': ''}) logger.debug('searching with params: {}', search_params) if config.get('free'): req = requests.get(FREE_SEARCH_URL, params=search_params, cookies={ 'uid': str(config['uid']), 'pass': config['password'] }) else: req = requests.get(SEARCH_URL, params=search_params, cookies={ 'uid': str(config['uid']), 'pass': config['password'] }) logger.debug('full search URL: {}', req.url) if '/u/' + str(config['uid']) not in req.text: raise plugin.PluginError( "Invalid cookies (user not logged in)...") soup = get_soup(req.content, parser="html.parser") torrents = soup.find('table', {'id': 'torrents'}) results = torrents.findAll('tr') for torrent in results: if torrent.th and 'ac' in torrent.th.get('class'): # Header column continue if torrent.find('td', {'colspan': '99'}): logger.debug('No results found for search {}', search_string) break entry = Entry() link = torrent.find('a', href=re.compile('download'))['href'] entry[ 'url'] = f"{BASE_URL}{link}?torrent_pass={config.get('rss_key')}" entry['title'] = torrent.find('a', href=re.compile('details')).text seeders = torrent.findNext('td', { 'class': 'ac t_seeders' }).text leechers = torrent.findNext('td', { 'class': 'ac t_leechers' }).text entry['torrent_seeds'] = int(seeders) entry['torrent_leeches'] = int(leechers) entry['torrent_availability'] = torrent_availability( entry['torrent_seeds'], entry['torrent_leeches']) size = torrent.findNext( text=re.compile(r'^([\.\d]+) ([GMK]?)B$')) size = re.search(r'^([\.\d]+) ([GMK]?)B$', size) entry['content_size'] = parse_filesize(size.group(0)) logger.debug('Found entry {}', entry) entries.add(entry) return entries
def search(self, task, entry, config=None): """ Search for name from iptorrents """ categories = config.get('category', 'All') # Make sure categories is a list if not isinstance(categories, list): categories = [categories] # If there are any text categories, turn them into their id number categories = [ c if isinstance(c, int) else CATEGORIES[c] for c in categories ] filter_url = '&'.join((str(c) + '=') for c in categories) entries = set() for search_string in entry.get('search_strings', [entry['title']]): query = normalize_unicode(search_string) query = quote_plus(query.encode('utf8')) url = "{base_url}/t?{filter}&q={query}&qf=".format( base_url=BASE_URL, filter=filter_url, query=query) logger.debug('searching with url: {}', url) req = requests.get(url, cookies={ 'uid': str(config['uid']), 'pass': config['password'] }) if '/u/' + str(config['uid']) not in req.text: raise plugin.PluginError( "Invalid cookies (user not logged in)...") soup = get_soup(req.content, parser="html.parser") torrents = soup.find('table', {'id': 'torrents'}) results = torrents.findAll('tr') for torrent in results: if torrent.th and 'ac' in torrent.th.get('class'): # Header column continue if torrent.find('td', {'colspan': '99'}): logger.debug('No results found for search {}', search_string) break entry = Entry() link = torrent.find('a', href=re.compile('download'))['href'] entry['url'] = "{base}{link}?torrent_pass={key}".format( base=BASE_URL, link=link, key=config.get('rss_key')) entry['title'] = torrent.find('a', href=re.compile('details')).text seeders = torrent.findNext('td', { 'class': 'ac t_seeders' }).text leechers = torrent.findNext('td', { 'class': 'ac t_leechers' }).text entry['torrent_seeds'] = int(seeders) entry['torrent_leeches'] = int(leechers) entry['torrent_availability'] = torrent_availability( entry['torrent_seeds'], entry['torrent_leeches']) size = torrent.findNext( text=re.compile(r'^([\.\d]+) ([GMK]?)B$')) size = re.search(r'^([\.\d]+) ([GMK]?)B$', size) entry['content_size'] = parse_filesize(size.group(0)) logger.debug('Found entry {}', entry) entries.add(entry) return entries
def search(self, task, entry, config=None): """ Search for name from torrentleech. """ request_headers = {'User-Agent': 'curl/7.54.0'} rss_key = config['rss_key'] # build the form request: data = {'username': config['username'], 'password': config['password']} # POST the login form: try: login = task.requests.post( 'https://www.torrentleech.org/user/account/login/', data=data, headers=request_headers, allow_redirects=True, ) except RequestException as e: raise PluginError('Could not connect to torrentleech: %s' % str(e)) if login.url.endswith('/user/account/login/'): raise PluginError('Could not login to torrentleech, faulty credentials?') if not isinstance(config, dict): config = {} # sort = SORT.get(config.get('sort_by', 'seeds')) # if config.get('sort_reverse'): # sort += 1 categories = config.get('category', 'all') # Make sure categories is a list if not isinstance(categories, list): categories = [categories] # If there are any text categories, turn them into their id number categories = [c if isinstance(c, int) else CATEGORIES[c] for c in categories] filter_url = '/categories/{}'.format(','.join(str(c) for c in categories)) entries = set() for search_string in entry.get('search_strings', [entry['title']]): query = normalize_unicode(search_string).replace(":", "") # urllib.quote will crash if the unicode string has non ascii characters, # so encode in utf-8 beforehand url = ( 'https://www.torrentleech.org/torrents/browse/list/query/' + quote(query.encode('utf-8')) + filter_url ) log.debug('Using %s as torrentleech search url', url) results = task.requests.get(url, headers=request_headers, cookies=login.cookies).json() for torrent in results['torrentList']: entry = Entry() entry['download_headers'] = request_headers entry['title'] = torrent['name'] # construct download URL torrent_url = 'https://www.torrentleech.org/rss/download/{}/{}/{}'.format( torrent['fid'], rss_key, torrent['filename'] ) log.debug('RSS-ified download link: %s', torrent_url) entry['url'] = torrent_url # seeders/leechers entry['torrent_seeds'] = torrent['seeders'] entry['torrent_leeches'] = torrent['leechers'] entry['torrent_availability'] = torrent_availability( entry['torrent_seeds'], entry['torrent_leeches'] ) entry['content_size'] = parse_filesize(str(torrent['size']) + ' b') entries.add(entry) return sorted(entries, reverse=True, key=lambda x: x.get('torrent_availability'))
def search(self, task, entry, config): """Search for entries on HEBits""" passkey = self.authenticate(config) params = {} if 'category' in config: params['cata'] = HEBitsCategory[config['category']].value entries = set() params['sort'] = HEBitsSort[config['order_by']].value params['type'] = 'desc' if config['order_desc'] else 'asc' for value in ('free', 'double', 'triple', 'pack'): if config.get(value): params[value] = 'on' for search_string in entry.get('search_strings', [entry['title']]): params['search'] = search_string logger.debug('Using search params: {}', params) try: page = requests.get(self.search_url, params=params) page.raise_for_status() except RequestException as e: logger.error('HEBits request failed: {}', e) continue soup = get_soup(page.content) table = first(soup.select("div.browse"), None) if not table: logger.debug( 'Could not find any results matching {} using the requested params {}', search_string, params, ) continue all_results = table.select( "div.lineBrown, div.lineGray, div.lineBlue, div.lineGreen") if not all_results: raise plugin.PluginError( 'Result table found but not with any known items, layout change?' ) for result in all_results: torrent_id = first(result.select( 'a[href^=download]')).attrs['href'].split('=')[-1] seeders = int(first(result.select("div.bUping")).text) leechers = int(first(result.select("div.bDowning")).text) size_strings = list(first(result.select("div.bSize")).strings) size_text = f'{size_strings[1]}{size_strings[2]}' size = parse_filesize(size_text) title = first( result.select('a > b')).text.split("/")[-1].strip() images = result.select("span > img") freeleech, double_up, triple_up = self._fetch_bonus(images) req = Request('GET', url=self.download_url, params={ 'passkey': passkey, 'id': torrent_id }).prepare() entry = Entry( torrent_seeds=seeders, torrent_leeches=leechers, torrent_availability=torrent_availability( seeders, leechers), content_size=size, title=title, freeleech=freeleech, triple_up=triple_up, double_up=double_up, url=req.url, ) entries.add(entry) return entries