From be9723e719c3c640a6d61f5817522ebc4fcd6675 Mon Sep 17 00:00:00 2001 From: JackDandy Date: Fri, 10 Jun 2016 01:28:37 +0100 Subject: [PATCH] Add Zooqle torrent provider. --- CHANGES.md | 3 +- sickbeard/helpers.py | 18 +++-- sickbeard/providers/__init__.py | 3 +- sickbeard/providers/generic.py | 4 +- sickbeard/providers/torrentday.py | 5 +- sickbeard/providers/zooqle.py | 113 ++++++++++++++++++++++++++++++ 6 files changed, 133 insertions(+), 13 deletions(-) create mode 100644 sickbeard/providers/zooqle.py diff --git a/CHANGES.md b/CHANGES.md index e8add6c1..dcd5af98 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -64,7 +64,7 @@ * Change instantly use saved value from Search Settings/Episode Search/"Check propers every" instead of after a restart * Change include OSError system messages in file system failure logs during post process * Fix find associated meta files to prevent orphan episode images -* Add torrent provider HD4Free +* Add HD4Free torrent provider * Change validate and improve specific Torrent provider connections, IPT, KAT, SCC, TPB, TB, TD, TT * Change refactor cache for torrent providers to reduce code * Change improve search category selection BMTV, FSH, FF, TB @@ -80,6 +80,7 @@ * Add button 'Discover' Emby server to notifications * Add Bit-HDTV torrent provider * Add PrivateHD torrent provider +* Add Zooqle torrent provider ### 0.11.11 (2016-04-05 19:20:00 UTC) diff --git a/sickbeard/helpers.py b/sickbeard/helpers.py index abc98316..485168a2 100644 --- a/sickbeard/helpers.py +++ b/sickbeard/helpers.py @@ -1190,21 +1190,23 @@ def getURL(url, post_data=None, params=None, headers=None, timeout=30, session=N return except requests.exceptions.HTTPError as e: - logger.log(u'HTTP error %s while loading URL %s' % (e.errno, e.request.url), logger.WARNING) + logger.log(u'HTTP error %s while loading URL%s' % ( + e.errno, _maybe_request_url(e)), logger.WARNING) return except requests.exceptions.ConnectionError as e: if not mute_connect_err: - logger.log(u'Connection error msg:%s while loading URL %s' % (e.message, e.request.url), logger.WARNING) + logger.log(u'Connection error msg:%s while loading URL%s' % ( + e.message, _maybe_request_url(e)), logger.WARNING) return except requests.exceptions.ReadTimeout as e: - logger.log(u'Read timed out msg:%s while loading URL %s' % (e.message, e.request.url), logger.WARNING) + logger.log(u'Read timed out msg:%s while loading URL%s' % ( + e.message, _maybe_request_url(e)), logger.WARNING) return except (requests.exceptions.Timeout, socket.timeout) as e: - logger.log(u'Connection timed out msg:%s while loading URL %s' - % (e.message, hasattr(e, 'request') and e.request.url or url), logger.WARNING) + logger.log(u'Connection timed out msg:%s while loading URL %s' % ( + e.message, _maybe_request_url(e, url)), logger.WARNING) return except Exception as e: - url = hasattr(e, 'request') and e.request.url or url if e.message: logger.log(u'Exception caught while loading URL %s\r\nDetail... %s\r\n%s' % (url, e.message, traceback.format_exc()), logger.WARNING) @@ -1223,6 +1225,10 @@ def getURL(url, post_data=None, params=None, headers=None, timeout=30, session=N return resp.content +def _maybe_request_url(e, def_url=''): + return hasattr(e, 'request') and hasattr(e.request, 'url') and ' ' + e.request.url or def_url + + def download_file(url, filename, session=None): # create session if None is session: diff --git a/sickbeard/providers/__init__.py b/sickbeard/providers/__init__.py index 28fddf74..8db1ca4b 100755 --- a/sickbeard/providers/__init__.py +++ b/sickbeard/providers/__init__.py @@ -28,7 +28,7 @@ from . import newznab, omgwtfnzbs, womble # torrent from . import alpharatio, beyondhd, bithdtv, bitmetv, btn, filelist, freshontv, funfile, gftracker, grabtheinfo, \ hd4free, hdbits, hdspace, iptorrents, kat, morethan, pisexy, pretome, privatehd, rarbg, scc, scenetime, shazbat, speedcd, \ - thepiratebay, torrentbytes, torrentday, torrenting, torrentleech, torrentshack, transmithe_net, tvchaosuk + thepiratebay, torrentbytes, torrentday, torrenting, torrentleech, torrentshack, transmithe_net, tvchaosuk, zooqle # anime from . import anizb, nyaatorrents, tokyotoshokan # custom @@ -73,6 +73,7 @@ __all__ = ['omgwtfnzbs', 'torrentshack', 'transmithe_net', 'tvchaosuk', + 'zooqle', 'nyaatorrents', 'tokyotoshokan', ] diff --git a/sickbeard/providers/generic.py b/sickbeard/providers/generic.py index f6beb83a..61703ab7 100644 --- a/sickbeard/providers/generic.py +++ b/sickbeard/providers/generic.py @@ -994,10 +994,10 @@ class TorrentProvider(object, GenericProvider): @staticmethod def _has_no_results(*html): - return re.search(r'(?i)<(?:b|div|h\d|span|strong)[^>]*>(?:' + + return re.search(r'(?i)<(?:b|div|h\d|p|span|strong)[^>]*>(?:' + 'your\ssearch\sdid\snot\smatch|' + 'nothing\sfound|' + - 'no\storrents\sfound|' + + '(sorry,\s)?no\storrents\s(found|match)|' + '.*?there\sare\sno\sresults|' + '.*?no\shits\.\sTry\sadding' + ')', html[0]) diff --git a/sickbeard/providers/torrentday.py b/sickbeard/providers/torrentday.py index 740f0447..a25aa667 100644 --- a/sickbeard/providers/torrentday.py +++ b/sickbeard/providers/torrentday.py @@ -10,7 +10,7 @@ # SickGear is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with SickGear. If not, see . @@ -102,8 +102,7 @@ class TorrentDayProvider(generic.TorrentProvider): except Exception: time.sleep(1.1) - self._log_search(mode, len(items[mode]) - cnt, - ('search string: ' + search_string, self.name)['Cache' == mode]) + self._log_search(mode, len(items[mode]) - cnt, search_url) self._sort_seeders(mode, items) diff --git a/sickbeard/providers/zooqle.py b/sickbeard/providers/zooqle.py new file mode 100644 index 00000000..68c73054 --- /dev/null +++ b/sickbeard/providers/zooqle.py @@ -0,0 +1,113 @@ +# coding=utf-8 +# +# This file is part of SickGear. +# +# SickGear is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# SickGear is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with SickGear. If not, see . + +import re +import traceback + +from . import generic +from sickbeard import logger +from sickbeard.bs4_parser import BS4Parser +from sickbeard.helpers import tryInt +from lib.unidecode import unidecode + + +class ZooqleProvider(generic.TorrentProvider): + + def __init__(self): + generic.TorrentProvider.__init__(self, 'Zooqle') + + self.url_base = 'https://zooqle.com/' + self.urls = {'config_provider_home_uri': self.url_base, + 'search': self.url_base + 'search?q=%s category:%s&s=dt&v=t&sd=d', + 'get': self.url_base + 'download/%s.torrent'} + + self.categories = {'Season': ['TV'], 'Episode': ['TV'], 'anime': ['Anime']} + self.categories['Cache'] = self.categories['Episode'] + + self.url = self.urls['config_provider_home_uri'] + + self.minseed, self.minleech = 2 * [None] + + def _search_provider(self, search_params, **kwargs): + + results = [] + + items = {'Cache': [], 'Season': [], 'Episode': [], 'Propers': []} + + rc = dict((k, re.compile('(?i)' + v)) for (k, v) in { + 'abd': '(\d{4}(?:[.]\d{2}){2})', 'peers': 'Seed[^\d]*(\d+)[\w\W]*?Leech[^\d]*(\d+)', + 'info': '(\w+)[.]html'}.items()) + for mode in search_params.keys(): + for search_string in search_params[mode]: + search_string = isinstance(search_string, unicode) and unidecode(search_string) or search_string + search_string = '+'.join(rc['abd'].sub(r'%22\1%22', search_string).split()) + search_url = self.urls['search'] % (search_string, self._categories_string(mode, '', ',')) + + html = self.get_url(search_url) + + cnt = len(items[mode]) + try: + if not html or self._has_no_results(html): + raise generic.HaltParseException + html = html.replace(' ', '').replace('"href=', '" href=').replace('"style', '" style') + with BS4Parser(html, features=['html5lib', 'permissive']) as soup: + torrent_table = soup.find('table', class_='table-torrents') + torrent_rows = [] if not torrent_table else torrent_table.find_all('tr') + + if 2 > len(torrent_rows): + raise generic.HaltParseException + + for tr in torrent_rows[1:]: + try: + td = tr.find_all('td') + stats = rc['peers'].findall((td[-1].find(class_='progress') or {}).get('title', '')) + seeders, leechers = any(stats) and [tryInt(x) for x in stats[0]] or (0, 0) + if self._peers_fail(mode, seeders, leechers): + continue + + info = td[1].find('a', href=rc['info']) + title = info and info.get_text().strip() + size = td[-3].get_text().strip() + + download_url = info and (self.urls['get'] % rc['info'].findall(info['href'])[0]) + + except (AttributeError, TypeError, ValueError, IndexError): + continue + + if title and download_url: + items[mode].append((title, download_url, seeders, self._bytesizer(size))) + + except generic.HaltParseException: + pass + except Exception: + logger.log(u'Failed to parse. Traceback: %s' % traceback.format_exc(), logger.ERROR) + + self._log_search(mode, len(items[mode]) - cnt, search_url) + + self._sort_seeders(mode, items) + + results = list(set(results + items[mode])) + + return results + + def _episode_strings(self, ep_obj, **kwargs): + return generic.TorrentProvider._episode_strings(self, ep_obj, sep_date='.', **kwargs) + + def _cache_data(self): + return self._search_provider({'Cache': ['*']}) + +provider = ZooqleProvider()