Merge pull request #785 from JackDandy/feature/AddProvHDT

Add HD-Torrents provider.
This commit is contained in:
JackDandy 2016-10-01 01:13:50 +01:00 committed by GitHub
commit 17bf73e5ae
24 changed files with 393 additions and 187 deletions

View file

@ -87,14 +87,16 @@
* Add DigitalHive torrent provider
* Add RevTT torrent provider
* Add PTF torrent provider
* Add ILT torrent provider
* Add Fano torrent provider
* Add BTScene torrent provider
* Add Extratorrent provider
* Add Limetorrents provider
* Add HD-Torrents provider
* Add nCore torrent provider
* Add TorLock provider
* Add Torrentz2 provider
* Add freeleech options to fano, freshon, hdspace, phd, ptf providers
* Change SceneTime to cookie auth
* Remove Usenet-Crawler provider
* Change CPU throttling on General Config/Advanced to "Disabled" by default for new installs
* Change provider OMGWTFNZBS api url and auto reject nuked releases
@ -180,6 +182,7 @@
* Add handler for when rar files can not be opened during post processing
* Fix join clean up
* Fix add custom torrent RSS
* Remove ILT torrent provider
### 0.11.15 (2016-09-13 19:50:00 UTC)

View file

@ -2290,7 +2290,7 @@ config*.tmpl
}
#config label.space-right{
margin-right:20px
margin-right:16px
}
#config .metadataDiv{

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -520,6 +520,23 @@ name = '' if not client else get_client_instance(sickbeard.TORRENT_METHOD)().nam
</label>
</div>
#end if
#if $hasattr($cur_torrent_provider, 'may_filter'):
<div class="field-pair">
<span class="component-title">Allow releases that are</span>
<span class="component-desc">
#for $cur_fval, $filter in $cur_torrent_provider.may_filter.iteritems()
#set $cur_fname, $cur_is_default = $filter[0], $filter[1]
#set $filter_id = '%s_filter_%s' % ($cur_torrent_provider.get_id(), $cur_fval)
<label class="space-right">
<input type="checkbox" name="$filter_id" id="$filter_id" #echo ('', $html_checked)[$cur_fval in $cur_torrent_provider.filter]#/>
<span>$cur_fname</span>
</label>
#end for
<span>(see site for meaning)</span>
<p>nothing selected allows everything (no filter, default)</p>
</span>
</div>
#end if
#if $hasattr($cur_torrent_provider, 'reject_m2ts'):
<div class="field-pair">
<label for="${cur_torrent_provider.get_id()}_reject_m2ts">

View file

@ -1080,6 +1080,8 @@ def initialize(consoleLogging=True):
torrent_prov.search_mode = check_setting_str(CFG, prov_id_uc, prov_id + '_search_mode', 'eponly')
if hasattr(torrent_prov, 'search_fallback'):
torrent_prov.search_fallback = bool(check_setting_int(CFG, prov_id_uc, prov_id + '_search_fallback', 0))
if hasattr(torrent_prov, 'filter'):
torrent_prov.filter = check_setting_str(CFG, prov_id_uc, prov_id + '_filter', [])
for nzb_prov in [curProvider for curProvider in providers.sortedProviderList()
if GenericProvider.NZB == curProvider.providerType]:
@ -1594,6 +1596,8 @@ def save_config():
if hasattr(src, '_seed_ratio'):
new_config[src_id_uc][src_id + '_seed_ratio'] = src.seed_ratio()
if hasattr(src, 'filter'):
new_config[src_id_uc][src_id + '_filter'] = src.filter
for src in [x for x in providers.sortedProviderList() if GenericProvider.NZB == x.providerType]:
src_id = src.get_id()

View file

@ -27,8 +27,8 @@ from sickbeard import logger, encodingKludge as ek
from . import newznab, omgwtfnzbs, womble
# torrent
from . import alpharatio, beyondhd, bithdtv, bitmetv, btn, btscene, dh, extratorrent, \
fano, filelist, freshontv, funfile, gftracker, grabtheinfo, hd4free, hdbits, hdspace, \
ilt, iptorrents, limetorrents, morethan, ncore, pisexy, pretome, privatehd, ptf, \
fano, filelist, freshontv, funfile, gftracker, grabtheinfo, hd4free, hdbits, hdspace, hdtorrents, \
iptorrents, limetorrents, morethan, ncore, pisexy, pretome, privatehd, ptf, \
rarbg, revtt, scc, scenetime, shazbat, speedcd, \
thepiratebay, torlock, torrentbytes, torrentday, torrenting, torrentleech, \
torrentshack, torrentz2, transmithe_net, tvchaosuk, zooqle
@ -61,7 +61,7 @@ __all__ = ['omgwtfnzbs',
'hd4free',
'hdbits',
'hdspace',
'ilt',
'hdtorrents',
'iptorrents',
'limetorrents',
'morethan',

View file

@ -34,9 +34,8 @@ class BeyondHDProvider(generic.TorrentProvider):
'browse': self.url_base + 'api_tv.php?passkey=%s&cats=%s',
'search': '&search=%s'}
self.categories = {'Season': '89',
'Episode': '40,44,48,43,45',
'Cache': '40,44,48,89,43,45'}
self.categories = {'Season': '89', 'Episode': '40,44,48,46,45'}
self.categories['Cache'] = '%s,%s' % (self.categories['Episode'], self.categories['Season'])
self.url = self.urls['config_provider_home_uri']

View file

@ -36,7 +36,7 @@ class BitHDTVProvider(generic.TorrentProvider):
self.url_tmpl = {'config_provider_home_uri': '%(home)s', 'login_action': '%(home)s%(vars)s',
'search': '%(home)s%(vars)s', 'get': '%(home)s%(vars)s'}
self.categories = {'Season': [12], 'Episode': [4, 5, 10], 'Anime': [1]}
self.categories = {'Season': [12], 'Episode': [4, 5, 10], 'anime': [1]}
self.categories['Cache'] = self.categories['Season'] + self.categories['Episode']
self.username, self.password, self.freeleech, self.minseed, self.minleech = 5 * [None]

View file

@ -15,6 +15,10 @@
# You should have received a copy of the GNU General Public License
# along with SickGear. If not, see <http://www.gnu.org/licenses/>.
try:
from collections import OrderedDict
except ImportError:
from requests.compat import OrderedDict
import re
import traceback
@ -24,6 +28,8 @@ from sickbeard.bs4_parser import BS4Parser
from sickbeard.helpers import tryInt
from lib.unidecode import unidecode
FLTAG = '</a>\s+<img[^>]+%s[^<]+<br'
class FanoProvider(generic.TorrentProvider):
@ -32,16 +38,19 @@ class FanoProvider(generic.TorrentProvider):
self.url_base = 'https://www.fano.in/'
self.urls = {'config_provider_home_uri': self.url_base,
'login_action': self.url_base + 'login.php',
'search': self.url_base + 'browse_old.php?search=%s&%s&incldead=0%s',
'get': self.url_base + '%s'}
'login_action': self.url_base + 'login.php', 'get': self.url_base + '%s',
'search': self.url_base + 'browse_old.php?search=%s&%s&incldead=0'}
self.categories = {'Season': [49], 'Episode': [6, 23, 32, 35], 'anime': [27]}
self.categories['Cache'] = self.categories['Season'] + self.categories['Episode']
self.url = self.urls['config_provider_home_uri']
self.username, self.password, self.freeleech, self.minseed, self.minleech = 5 * [None]
self.filter = []
self.may_filter = OrderedDict([
('f0', ('not marked', False, '')), ('fx2', ('x2', True, FLTAG % 'x2_up')),
('fgx2', ('gold/x2', True, FLTAG % 'free[^<]+<img[^>]+x2_up')), ('fg', ('gold', True, FLTAG % 'free'))])
self.username, self.password, self.minseed, self.minleech = 4 * [None]
def _authorised(self, **kwargs):
@ -57,13 +66,21 @@ class FanoProvider(generic.TorrentProvider):
rc = dict((k, re.compile('(?i)' + v))
for (k, v) in {'abd': '(\d{4}(?:[.]\d{2}){2})', 'info': 'details', 'get': 'download'}.items())
log = ''
if self.filter:
non_marked = 'f0' in self.filter
# if search_any, use unselected to exclude, else use selected to keep
filters = ([f for f in self.may_filter if f in self.filter],
[f for f in self.may_filter if f not in self.filter])[non_marked]
rc['filter'] = re.compile('(?i)(%s)' % '|'.join(
[self.may_filter[f][2] for f in filters if self.may_filter[f][1]]))
log = '%sing (%s) ' % (('keep', 'skipp')[non_marked], ', '.join([self.may_filter[f][0] for f in filters]))
for mode in search_params.keys():
rc['cats'] = re.compile('(?i)cat=(?:%s)' % self._categories_string(mode, template='', delimiter='|'))
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),
('&sgold=on', '')[not self.freeleech])
search_url = self.urls['search'] % (search_string, self._categories_string(mode))
html = self.get_url(search_url)
@ -80,6 +97,10 @@ class FanoProvider(generic.TorrentProvider):
raise generic.HaltParseException
for tr in torrent_rows[1:]:
if (any(self.filter)
and ((non_marked and rc['filter'].search(str(tr)))
or (not non_marked and not rc['filter'].search(str(tr))))):
continue
try:
seeders, leechers, size = [tryInt(n, n) for n in [
tr.find_all('td')[x].get_text().strip() for x in -2, -1, -4]]
@ -99,7 +120,7 @@ class FanoProvider(generic.TorrentProvider):
except (StandardError, 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._log_search(mode, len(items[mode]) - cnt, log + search_url)
results = self._sort_seeding(mode, results + items[mode])

View file

@ -15,6 +15,10 @@
# You should have received a copy of the GNU General Public License
# along with SickGear. If not, see <http://www.gnu.org/licenses/>.
try:
from collections import OrderedDict
except ImportError:
from requests.compat import OrderedDict
import re
import traceback
@ -33,14 +37,17 @@ class FreshOnTVProvider(generic.TorrentProvider):
self.url_base = 'https://freshon.tv/'
self.urls = {'config_provider_home_uri': self.url_base,
'login_action': self.url_base + 'login.php',
'search': self.url_base + 'browse.php?incldead=%s&words=0&%s&search=%s',
'search': self.url_base + 'browse.php?incldead=0&words=0&%s&search=%s',
'get': self.url_base + '%s'}
self.categories = {'shows': 0, 'anime': 235}
self.url = self.urls['config_provider_home_uri']
self.username, self.password, self.freeleech, self.minseed, self.minleech = 5 * [None]
self.filter = []
self.may_filter = OrderedDict([
('f0', ('not marked', False, '')), ('f50', ('50%', True)), ('f100', ('100%', True))])
self.username, self.password, self.minseed, self.minleech = 4 * [None]
def _authorised(self, **kwargs):
@ -59,17 +66,25 @@ class FreshOnTVProvider(generic.TorrentProvider):
return results
items = {'Cache': [], 'Season': [], 'Episode': [], 'Propers': []}
freeleech = (3, 0)[not self.freeleech]
rc = dict((k, re.compile('(?i)' + v))
for (k, v) in {'info': 'detail', 'get': 'download', 'name': '_name'}.items())
log = ''
if self.filter:
non_marked = 'f0' in self.filter
# if search_any, use unselected to exclude, else use selected to keep
filters = ([f for f in self.may_filter if f in self.filter],
[f for f in self.may_filter if f not in self.filter])[non_marked]
rc['filter'] = re.compile('(?i)(%s).png' % '|'.join(
[f.replace('f', '') for f in filters if self.may_filter[f][1]]))
log = '%sing (%s) ' % (('keep', 'skipp')[non_marked], ', '.join([self.may_filter[f][0] for f in filters]))
for mode in search_params.keys():
for search_string in search_params[mode]:
search_string, void = self._title_and_url((
isinstance(search_string, unicode) and unidecode(search_string) or search_string, ''))
void, search_url = self._title_and_url((
'', self.urls['search'] % (freeleech, self._categories_string(mode, 'cat=%s'), search_string)))
'', self.urls['search'] % (self._categories_string(mode, 'cat=%s'), search_string)))
# returns top 15 results by default, expandable in user profile to 100
html = self.get_url(search_url)
@ -87,10 +102,12 @@ class FreshOnTVProvider(generic.TorrentProvider):
raise generic.HaltParseException
for tr in torrent_rows[1:]:
if (tr.find('img', alt='Nuked')
or (any(self.filter)
and ((non_marked and tr.find('img', src=rc['filter']))
or (not non_marked and not tr.find('img', src=rc['filter']))))):
continue
try:
if tr.find('img', alt='Nuked'):
continue
seeders, leechers, size = [tryInt(n, n) for n in [
tr.find_all('td')[x].get_text().strip() for x in -2, -1, -4]]
if self._peers_fail(mode, seeders, leechers):
@ -109,7 +126,7 @@ class FreshOnTVProvider(generic.TorrentProvider):
pass
except (StandardError, 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._log_search(mode, len(items[mode]) - cnt, log + search_url)
results = self._sort_seeding(mode, results + items[mode])

View file

@ -961,6 +961,7 @@ class TorrentProvider(object, GenericProvider):
for i in range(0, len(url)):
helpers.getURL(url.pop(), session=self.session)
passfield, userfield = None, None
if not url:
if hasattr(self, 'urls'):
url = self.urls.get('login_action')
@ -980,12 +981,13 @@ class TorrentProvider(object, GenericProvider):
(url + action) if action.startswith('?') else \
(self.urls.get('login_base') or self.urls['config_provider_home_uri']) + action.lstrip('/')
tags = re.findall(r'(?is)(<input.*?name=[\'"][^\'"]+[^>]*)', response)
nv = [(tup[0]) for tup in [
re.findall(r'(?is)name=[\'"]([^\'"]+)(?:[^>]*?value=[\'"]([^\'"]+))?', x)
for x in tags]]
for name, value in nv:
if name not in ('username', 'password'):
tags = re.findall(r'(?is)(<input[^>]*?name=[\'"][^\'"]+[^>]*)', response)
attrs = [[(re.findall(r'(?is)%s=[\'"]([^\'"]+)' % attr, x) or [''])[0]
for attr in ['type', 'name', 'value']] for x in tags]
for itype, name, value in attrs:
if 'password' in [itype, name]:
passfield = name
if name not in ('username', 'password') and 'password' != itype:
post_params.setdefault(name, value)
except KeyError:
return super(TorrentProvider, self)._authorised()
@ -1001,7 +1003,7 @@ class TorrentProvider(object, GenericProvider):
if self.username not in post_params.values():
post_params['username'] = self.username
if self.password not in post_params.values():
post_params['password'] = self.password
post_params[(passfield, 'password')[not passfield]] = self.password
response = helpers.getURL(url, post_data=post_params, session=self.session, timeout=timeout)
if response:
@ -1074,10 +1076,10 @@ class TorrentProvider(object, GenericProvider):
@staticmethod
def _has_no_results(*html):
return re.search(r'(?i)<(?:b|div|h\d|p|span|strong)[^>]*>\s*(?:' +
return re.search(r'(?i)<(?:b|div|h\d|p|span|strong|td)[^>]*>\s*(?:' +
'your\ssearch.*?did\snot\smatch|' +
'(?:nothing|0</b>\s+torrents)\sfound|' +
'(sorry,\s)?no\s(?:results|torrents)\s(found|match)|' +
'(sorry,\s)?no\s(?:results|torrents)\s(found|here|match)|' +
'.*?there\sare\sno\sresults|' +
'.*?no\shits\.\sTry\sadding' +
')', html[0])

View file

@ -37,7 +37,7 @@ class GrabTheInfoProvider(generic.TorrentProvider):
'search': '&search=%s',
'get': self.url_base + '%s'}
self.categories = {'shows': [36, 32, 43, 56, 8, 10, 61]}
self.categories = {'shows': [36, 32, 43, 56, 8, 65, 61, 10]}
self.url = self.urls['config_provider_home_uri']

View file

@ -15,6 +15,10 @@
# You should have received a copy of the GNU General Public License
# along with SickGear. If not, see <http://www.gnu.org/licenses/>.
try:
from collections import OrderedDict
except ImportError:
from requests.compat import OrderedDict
import re
import traceback
@ -42,12 +46,15 @@ class HDSpaceProvider(generic.TorrentProvider):
self.url = self.urls['config_provider_home_uri']
self.username, self.password, self.freeleech, self.minseed, self.minleech = 5 * [None]
self.filter = []
self.may_filter = OrderedDict([
('f0', ('not marked', False, '')), ('f25', ('FL', True, 'gold|sf')), ('f50', ('F/L', True, 'silver|sf'))])
self.username, self.password, self.minseed, self.minleech = 4 * [None]
def _authorised(self, **kwargs):
return super(HDSpaceProvider, self)._authorised(
post_params={'uid': self.username, 'pwd': self.password, 'form_tmpl': 'name=[\'"]login[\'"]'})
post_params={'uid': self.username, 'form_tmpl': 'name=[\'"]login[\'"]'})
def _search_provider(self, search_params, **kwargs):
@ -57,8 +64,17 @@ class HDSpaceProvider(generic.TorrentProvider):
items = {'Cache': [], 'Season': [], 'Episode': [], 'Propers': []}
rc = dict((k, re.compile('(?i)' + v)) for (k, v) in {'info': 'torrent-details', 'get': 'download', 'fl': 'free',
'peers': 'page=peers', 'nodots': '[\.\s]+'}.items())
rc = dict((k, re.compile('(?i)' + v)) for (k, v) in {
'info': 'torrent-details', 'get': 'download', 'peers': 'page=peers', 'nodots': '[\.\s]+'}.items())
log = ''
if self.filter:
non_marked = 'f0' in self.filter
# if search_any, use unselected to exclude, else use selected to keep
filters = ([f for f in self.may_filter if f in self.filter],
[f for f in self.may_filter if f not in self.filter])[non_marked]
rc['filter'] = re.compile('(?i)(%s).png' % '|'.join(
[self.may_filter[f][2] for f in filters if self.may_filter[f][1]]))
log = '%sing (%s) ' % (('keep', 'skipp')[non_marked], ', '.join([self.may_filter[f][0] for f in filters]))
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
@ -83,7 +99,10 @@ class HDSpaceProvider(generic.TorrentProvider):
raise generic.HaltParseException
for tr in torrent_rows[1:]:
if tr.find('td', class_='header'):
if (tr.find('td', class_='header')
or (any(self.filter)
and ((non_marked and tr.find('img', src=rc['filter']))
or (not non_marked and not tr.find('img', src=rc['filter']))))):
continue
downlink = tr.find('a', href=rc['get'])
if None is downlink:
@ -91,8 +110,7 @@ class HDSpaceProvider(generic.TorrentProvider):
try:
seeders, leechers = [tryInt(x.get_text().strip())
for x in tr.find_all('a', href=rc['peers'])]
if self._peers_fail(mode, seeders, leechers)\
or self.freeleech and None is tr.find('img', title=rc['fl']):
if self._peers_fail(mode, seeders, leechers):
continue
info = tr.find('a', href=rc['info'])
@ -109,7 +127,7 @@ class HDSpaceProvider(generic.TorrentProvider):
pass
except (StandardError, 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._log_search(mode, len(items[mode]) - cnt, log + search_url)
results = self._sort_seeding(mode, results + items[mode])

View file

@ -0,0 +1,145 @@
# 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 <http://www.gnu.org/licenses/>.
try:
from collections import OrderedDict
except ImportError:
from requests.compat import OrderedDict
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 HDTorrentsProvider(generic.TorrentProvider):
def __init__(self):
generic.TorrentProvider.__init__(self, 'HDTorrents')
self.url_home = ['https://hd-torrents.%s/' % x for x in 'org', 'net', 'me'] + ['https://hdts.ru/']
self.url_vars = {'login_action': 'index.php',
'search': 'torrents.php?search=%s&active=0&options=0&%s', 'get': '%s'}
self.url_tmpl = {'config_provider_home_uri': '%(home)s', 'login_action': '%(home)s%(vars)s',
'search': '%(home)s%(vars)s', 'get': '%(home)s%(vars)s'}
self.categories = {'Episode': [59, 60, 30, 38, 65], 'anime': ['Animation']}
self.categories['Season'] = self.categories['Cache'] = self.categories['Episode']
self.filter = []
self.may_filter = OrderedDict(
[('f0', ('not marked', False)), ('f25', ('-25%', True)), ('f50', ('-50%', True)), ('f75', ('-75%', True))])
self.username, self.password, self.minseed, self.minleech = 4 * [None]
def _authorised(self, **kwargs):
return super(HDTorrentsProvider, self)._authorised(post_params={'uid': self.username})
@staticmethod
def _has_signature(data=None):
return generic.TorrentProvider._has_signature(data) or \
(data and re.search(r'(?i)<title[^<]+?(HD-Torrents)', data))
def _search_provider(self, search_params, **kwargs):
results = []
if not self._authorised():
return results
items = {'Cache': [], 'Season': [], 'Episode': [], 'Propers': []}
rc = dict((k, re.compile('(?i)' + v)) for (k, v) in {'info': 'details', 'get': 'download'}.items())
log = ''
if self.filter:
non_marked = 'f0' in self.filter
# if search_any, use unselected to exclude, else use selected to keep
filters = ([f for f in self.may_filter if f in self.filter],
[f for f in self.may_filter if f not in self.filter])[non_marked]
rc['filter'] = re.compile('(?i)(%s).png' % '|'.join(
[f.replace('f', '') for f in filters if self.may_filter[f][1]]))
log = '%sing (%s) ' % (('keep', 'skipp')[non_marked], ', '.join([self.may_filter[f][0] for f in filters]))
for mode in search_params.keys():
rc['cats'] = re.compile('(?i)category=(?:%s)' % self._categories_string(mode, template='', delimiter='|'))
for search_string in search_params[mode]:
search_string = isinstance(search_string, unicode) and unidecode(search_string) or search_string
search_url = self.urls['search'] % (
search_string,
self._categories_string(mode, template='category[]=%s')
.replace('&category[]=Animation', ('&genre[]=Animation', '')[mode in ['Cache', 'Propers']]))
html = self.get_url(search_url)
cnt = len(items[mode])
try:
if not html or self._has_no_results(html):
raise generic.HaltParseException
html = re.sub('(?ims)<div[^>]+display:\s*none;.*?</div>', '', html)
html = re.sub('(?im)href=([^\\"][^>]+)>', r'href="\1">', html)
html = (html.replace('"/></td>', '" /></a></td>')
.replace('"title="', '" title="')
.replace('</u></span></a></td>', '</u></a></span></td>'))
html = re.sub('(?im)<b([mtwfs][^>]+)', r'<b>\1</b', html)
with BS4Parser(html, features=['html5lib', 'permissive'], attr='width="100%"') as soup:
torrent_rows = [tr for tr in ([] if not soup else soup.find_all('tr'))
if tr.find('a', href=rc['info'])]
if not len(torrent_rows):
raise generic.HaltParseException
for tr in torrent_rows:
if (any(self.filter)
and ((non_marked and tr.find('img', src=rc['filter']))
or (not non_marked and not tr.find('img', src=rc['filter'])))):
continue
try:
seeders, leechers, size = [tryInt(n, n) for n in [
tr.find_all('td')[x].get_text().strip() for x in -3, -2, -5]]
if self._peers_fail(mode, seeders, leechers) or not tr.find('a', href=rc['cats']):
continue
title = tr.find('a', href=rc['info']).get_text().strip()
download_url = self._link(tr.find('a', href=rc['get'])['href'])
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 (StandardError, Exception):
logger.log(u'Failed to parse. Traceback: %s' % traceback.format_exc(), logger.ERROR)
self._log_search(mode, len(items[mode]) - cnt, log + search_url)
results = self._sort_seeding(mode, results + items[mode])
return results
def _season_strings(self, ep_obj, **kwargs):
return generic.TorrentProvider._season_strings(self, ep_obj, scene=False, **kwargs)
def _episode_strings(self, ep_obj, **kwargs):
return generic.TorrentProvider._episode_strings(self, ep_obj, scene=False, sep_date=' ', **kwargs)
provider = HDTorrentsProvider()

View file

@ -1,106 +0,0 @@
# 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 <http://www.gnu.org/licenses/>.
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 ILTProvider(generic.TorrentProvider):
def __init__(self):
generic.TorrentProvider.__init__(self, 'ILoveTorrents')
self.url_base = 'https://www.ilovetorrents.me/'
self.urls = {'config_provider_home_uri': self.url_base,
'login_action': self.url_base + 'login.php',
'search': self.url_base + 'browse.php?search=%s&%s&incldead=0&blah=0',
'get': self.url_base + '%s'}
self.categories = {'Season': [5], 'Episode': [7, 8, 40], 'anime': [23]}
self.categories['Cache'] = self.categories['Season'] + self.categories['Episode']
self.url = self.urls['config_provider_home_uri']
self.username, self.password, self.minseed, self.minleech = 4 * [None]
def _authorised(self, **kwargs):
return super(ILTProvider, self)._authorised()
def _search_provider(self, search_params, **kwargs):
results = []
if not self._authorised():
return results
items = {'Cache': [], 'Season': [], 'Episode': [], 'Propers': []}
rc = dict((k, re.compile('(?i)' + v)) for (k, v) in {'info': 'details', 'get': 'download'}.items())
for mode in search_params.keys():
rc['cats'] = re.compile('(?i)cat=(?:%s)' % self._categories_string(mode, template='', delimiter='|'))
for search_string in search_params[mode]:
search_string = isinstance(search_string, unicode) and unidecode(search_string) or search_string
html = self.get_url(self.urls['search'] % ('+'.join(search_string.split()),
self._categories_string(mode)))
cnt = len(items[mode])
try:
if not html or self._has_no_results(html):
raise generic.HaltParseException
with BS4Parser(html, features=['html5lib', 'permissive']) as soup:
torrent_table = soup.find('table', 'koptekst')
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:
seeders, leechers, size = [tryInt(n, n) for n in [
tr.find_all('td')[x].get_text().strip() for x in -3, -2, -5]]
if self._peers_fail(mode, seeders, leechers) or not tr.find('a', href=rc['cats']):
continue
title = tr.find('a', href=rc['info']).get_text().strip()
download_url = self._link(tr.find('a', href=rc['get'])['href'])
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 (StandardError, Exception):
logger.log(u'Failed to parse. Traceback: %s' % traceback.format_exc(), logger.ERROR)
self._log_search(mode, len(items[mode]) - cnt, self.session.response.get('url'))
results = self._sort_seeding(mode, results + items[mode])
return results
provider = ILTProvider()

View file

@ -49,7 +49,7 @@ class NcoreProvider(generic.TorrentProvider):
return super(NcoreProvider, self)._authorised(
logged_in=(lambda y='': all([bool(y), 'action="login' not in y, self.has_all_cookies('PHPSESSID')])),
post_params={'nev': self.username, 'pass': self.password, 'form_tmpl': 'name=[\'"]login[\'"]'})
post_params={'nev': self.username, 'form_tmpl': 'name=[\'"]login[\'"]'})
def _search_provider(self, search_params, **kwargs):

View file

@ -36,7 +36,7 @@ class PiSexyProvider(generic.TorrentProvider):
self.url = self.urls['config_provider_home_uri']
self.username, self.password, self.minseed, self.minleech = 4 * [None]
self.username, self.password, self.freeleech, self.minseed, self.minleech = 5 * [None]
def _authorised(self, **kwargs):
@ -51,9 +51,9 @@ class PiSexyProvider(generic.TorrentProvider):
items = {'Cache': [], 'Season': [], 'Episode': [], 'Propers': []}
rc = dict((k, re.compile('(?i)' + v))
for (k, v) in {'info': 'download', 'get': 'download', 'valid_cat': 'cat=(?:0|50[12])',
'title': r'Download\s([^\s]+).*', 'seeders': r'(^\d+)', 'leechers': r'(\d+)$'}.items())
rc = dict((k, re.compile('(?i)' + v)) for (k, v) in {
'info': 'download', 'get': 'download', 'valid_cat': 'cat=(?:0|50[12])', 'filter': 'free',
'title': r'Download\s([^\s]+).*', 'seeders': r'(^\d+)', 'leechers': r'(\d+)$'}.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
@ -78,7 +78,8 @@ class PiSexyProvider(generic.TorrentProvider):
seeders, leechers = 2 * [tr.find_all('td')[-4].get_text().strip()]
seeders, leechers = [tryInt(n) for n in [
rc['seeders'].findall(seeders)[0], rc['leechers'].findall(leechers)[0]]]
if self._peers_fail(mode, seeders, leechers) or not tr.find('a', href=rc['valid_cat']):
if self._peers_fail(mode, seeders, leechers) or not tr.find('a', href=rc['valid_cat']) \
or (self.freeleech and not tr.find('img', src=rc['filter'])):
continue
info = tr.find('a', href=rc['info'])

View file

@ -15,6 +15,10 @@
# You should have received a copy of the GNU General Public License
# along with SickGear. If not, see <http://www.gnu.org/licenses/>.
try:
from collections import OrderedDict
except ImportError:
from requests.compat import OrderedDict
import re
import traceback
@ -35,13 +39,18 @@ class PrivateHDProvider(generic.TorrentProvider):
'login_action': self.url_base + 'auth/login',
'search': self.url_base + 'torrents?%s' % '&'.join(
['in=1', 'tags=', 'type=2', 'language=0', 'subtitle=0', 'rip_type=0',
'video_quality=0', 'uploader=', 'search=%s', 'tv_type[]=%s', 'discount[]=%s'])}
'video_quality=0', 'uploader=', 'search=%s', 'tv_type[]=%s'])}
self.categories = {'Season': [2], 'Episode': [1], 'Cache': [0]}
self.url = self.urls['config_provider_home_uri']
self.username, self.password, self.freeleech, self.minseed, self.minleech = 5 * [None]
self.filter = []
self.may_filter = OrderedDict([
('f0', ('not marked', False)), ('free', ('free', True)),
('half', ('50% down', True)), ('double', ('2x up', True))])
self.username, self.password, self.minseed, self.minleech = 4 * [None]
self.confirmed = False
def _authorised(self, **kwargs):
@ -59,6 +68,21 @@ class PrivateHDProvider(generic.TorrentProvider):
rc = dict((k, re.compile('(?i)' + v))
for (k, v) in {'info': '.*?details\s*-\s*', 'get': 'download'}.items())
log = ''
if self.filter:
non_marked = 'f0' in self.filter
# if search_any, use unselected to exclude, else use selected to keep
filters = ([f for f in self.may_filter if f in self.filter],
[f for f in self.may_filter if f not in self.filter])[non_marked]
filters += (((all([x in filters for x in 'free', 'double']) and ['freedouble'] or [])
+ (all([x in filters for x in 'half', 'double']) and ['halfdouble'] or [])),
((not all([x not in filters for x in 'free', 'double']) and ['freedouble'] or [])
+ (not all([x not in filters for x in 'half', 'double']) and ['halfdouble'] or []))
)[non_marked]
rc['filter'] = re.compile('(?i)^(%s)$' % '|'.join(
['%s' % f for f in filters if (f in self.may_filter and self.may_filter[f][1]) or f]))
log = '%sing (%s) ' % (('keep', 'skipp')[non_marked], ', '.join(
[f in self.may_filter and self.may_filter[f][0] or f for f in filters]))
for mode in search_params.keys():
if mode in ['Season', 'Episode']:
show_type = self.show.air_by_date and 'Air By Date' \
@ -70,7 +94,7 @@ class PrivateHDProvider(generic.TorrentProvider):
for search_string in search_params[mode]:
search_string = isinstance(search_string, unicode) and unidecode(search_string) or search_string
search_url = self.urls['search'] % (
'+'.join(search_string.split()), self._categories_string(mode, ''), (1, 0)[not self.freeleech])
'+'.join(search_string.split()), self._categories_string(mode, ''))
html = self.get_url(search_url)
@ -87,6 +111,15 @@ class PrivateHDProvider(generic.TorrentProvider):
raise generic.HaltParseException
for tr in torrent_rows[1:]:
if self.confirmed and tr.find('i', title=re.compile('(?i)unverified')):
continue
if any(self.filter):
marked = ','.join([x.attrs.get('title', '').lower() for x in tr.find_all(
'i', attrs={'class': ['fa-star', 'fa-diamond', 'fa-star-half-o']})])
munged = ''.join(filter(marked.__contains__, ['free', 'half', 'double']))
if ((non_marked and rc['filter'].search(munged)) or
(not non_marked and not rc['filter'].search(munged))):
continue
try:
seeders, leechers, size = [tryInt(n, n) for n in [
tr.find_all('td')[x].get_text().strip() for x in -3, -2, -4]]
@ -106,7 +139,7 @@ class PrivateHDProvider(generic.TorrentProvider):
except (StandardError, 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._log_search(mode, len(items[mode]) - cnt, log + search_url)
results = self._sort_seeding(mode, results + items[mode])

View file

@ -15,6 +15,10 @@
# You should have received a copy of the GNU General Public License
# along with SickGear. If not, see <http://www.gnu.org/licenses/>.
try:
from collections import OrderedDict
except ImportError:
from requests.compat import OrderedDict
import re
import time
import traceback
@ -22,7 +26,7 @@ import traceback
from . import generic
from sickbeard import logger
from sickbeard.bs4_parser import BS4Parser
from sickbeard.helpers import tryInt
from sickbeard.helpers import tryInt, anon_url
from lib.unidecode import unidecode
@ -33,9 +37,8 @@ class PTFProvider(generic.TorrentProvider):
self.url_base = 'https://ptfiles.net/'
self.urls = {'config_provider_home_uri': self.url_base,
'login_action': self.url_base + 'loginproc/',
'login_base': self.url_base + 'loginproc/',
'search': self.url_base + 'browse.php?search=%s&%s&incldead=0&title=0%s',
'login': self.url_base + 'panel.php?tool=links',
'search': self.url_base + 'browse.php?search=%s&%s&incldead=0&title=0',
'get': self.url_base + '%s'}
self.categories = {'Season': [39], 'Episode': [7, 33, 42], 'anime': [23]}
@ -43,12 +46,19 @@ class PTFProvider(generic.TorrentProvider):
self.url = self.urls['config_provider_home_uri']
self.username, self.password, self.freeleech, self.minseed, self.minleech = 5 * [None]
self.filter = []
self.may_filter = OrderedDict([
('f0', ('not marked', False, '')), ('free', ('free', True, '^free$')),
('freeday', ('free day', True, '^free[^!]+day')), ('freeweek', ('free week', True, '^free[^!]+week'))])
self.digest, self.minseed, self.minleech = 3 * [None]
def _authorised(self, **kwargs):
return super(PTFProvider, self)._authorised(logged_in=(lambda y=None: self.has_all_cookies('session_key')),
post_params={'force_ssl': 'on', 'ssl': '', 'form_tmpl': True})
return super(PTFProvider, self)._authorised(
logged_in=(lambda y='': all(
['RSS Feed' in y, self.has_all_cookies('session_key')] +
[(self.session.cookies.get(x) or 'sg!no!pw') in self.digest for x in ['session_key']])),
failed_msg=(lambda y=None: u'Invalid cookie details for %s. Check settings'))
def _search_provider(self, search_params, **kwargs):
@ -60,13 +70,21 @@ class PTFProvider(generic.TorrentProvider):
rc = dict((k, re.compile('(?i)' + v)) for (k, v) in {'info': 'details', 'get': 'dl.php', 'snatch': 'snatches',
'seeders': r'(^\d+)', 'leechers': r'(\d+)$'}.items())
log = ''
if self.filter:
non_marked = 'f0' in self.filter
# if search_any, use unselected to exclude, else use selected to keep
filters = ([f for f in self.may_filter if f in self.filter],
[f for f in self.may_filter if f not in self.filter])[non_marked]
rc['filter'] = re.compile('(?i)(%s)' % '|'.join(
[self.may_filter[f][2] for f in filters if self.may_filter[f][1]]))
log = '%sing (%s) ' % (('keep', 'skipp')[non_marked], ', '.join([self.may_filter[f][0] for f in filters]))
for mode in search_params.keys():
rc['cats'] = re.compile('(?i)cat=(?:%s)' % self._categories_string(mode, template='', delimiter='|'))
for search_string in search_params[mode]:
search_string = isinstance(search_string, unicode) and unidecode(search_string) or search_string
search_url = self.urls['search'] % ('+'.join(search_string.split()), self._categories_string(mode),
('&free=1', '')[not self.freeleech])
search_url = self.urls['search'] % ('+'.join(search_string.split()), self._categories_string(mode))
html = self.get_url(search_url)
time.sleep(2)
if not self.has_all_cookies(['session_key']):
@ -87,6 +105,15 @@ class PTFProvider(generic.TorrentProvider):
raise generic.HaltParseException
for tr in torrent_rows[1:]:
if any(self.filter):
marker = ''
try:
marker = tr.select('a[href^="browse"] .tip')[0].get_text().strip()
except (StandardError, Exception):
pass
if ((non_marked and rc['filter'].search(marker)) or
(not non_marked and not rc['filter'].search(marker))):
continue
try:
seeders, leechers = 2 * [tr.find_all('td')[-2].get_text().strip()]
seeders, leechers = [tryInt(n) for n in [
@ -110,11 +137,19 @@ class PTFProvider(generic.TorrentProvider):
except (StandardError, Exception):
logger.log(u'Failed to parse. Traceback: %s' % traceback.format_exc(), logger.ERROR)
self._log_search(mode, len(items[mode]) - cnt, self.session.response.get('url'))
self._log_search(mode, len(items[mode]) - cnt, log + self.session.response.get('url'))
results = self._sort_seeding(mode, results + items[mode])
return results
def ui_string(self, key):
if 'ptfiles_digest' == key and self._valid_home():
current_url = getattr(self, 'urls', {}).get('config_provider_home_uri')
return ('use... \'session_key=xx\'' +
(current_url and (' from a session logged in at <a target="_blank" href="%s">%s</a>' %
(anon_url(current_url), current_url.strip('/'))) or ''))
return ''
provider = PTFProvider()

View file

@ -22,7 +22,7 @@ import traceback
from . import generic
from sickbeard import logger
from sickbeard.bs4_parser import BS4Parser
from sickbeard.helpers import tryInt
from sickbeard.helpers import tryInt, anon_url
from lib.unidecode import unidecode
@ -31,22 +31,27 @@ class SceneTimeProvider(generic.TorrentProvider):
def __init__(self):
generic.TorrentProvider.__init__(self, 'SceneTime', cache_update_freq=15)
self.url_base = 'https://www.scenetime.com/'
self.urls = {'config_provider_home_uri': self.url_base,
'login_action': self.url_base + 'login.php',
'browse': self.url_base + 'browse_API.php',
'params': {'sec': 'jax', 'cata': 'yes'},
'get': self.url_base + 'download.php/%(id)s/%(title)s.torrent'}
self.url_home = ['https://%s.scenetime.com/' % u for u in 'www', 'uk']
self.url_vars = {'login': 'support.php', 'browse': 'browse_API.php', 'get': 'download.php/%s.torrent'}
self.url_tmpl = {'config_provider_home_uri': '%(home)s', 'login': '%(home)s%(vars)s',
'browse': '%(home)s%(vars)s', 'get': '%(home)s%(vars)s'}
self.categories = {'shows': [2, 43, 9, 63, 77, 79, 83]}
self.url = self.urls['config_provider_home_uri']
self.username, self.password, self.freeleech, self.minseed, self.minleech = 5 * [None]
self.digest, self.freeleech, self.minseed, self.minleech = 4 * [None]
def _authorised(self, **kwargs):
return super(SceneTimeProvider, self)._authorised(post_params={'form_tmpl': True})
return super(SceneTimeProvider, self)._authorised(
logged_in=(lambda y='': all(
['staff-support' in y, self.has_all_cookies()] +
[(self.session.cookies.get(x) or 'sg!no!pw') in self.digest for x in 'uid', 'pass'])),
failed_msg=(lambda y=None: u'Invalid cookie details for %s. Check settings'))
@staticmethod
def _has_signature(data=None):
return generic.TorrentProvider._has_signature(data) or (data and re.search(r'(?i)<title[^<]+?(Scenetim)', data))
def _search_provider(self, search_params, **kwargs):
@ -63,7 +68,7 @@ class SceneTimeProvider(generic.TorrentProvider):
for search_string in search_params[mode]:
search_string = isinstance(search_string, unicode) and unidecode(search_string) or search_string
post_data = self.urls['params'].copy()
post_data = {'sec': 'jax', 'cata': 'yes'}
post_data.update(ast.literal_eval(
'{%s}' % self._categories_string(template='"c%s": "1"', delimiter=',')))
if 'Cache' != mode:
@ -99,9 +104,8 @@ class SceneTimeProvider(generic.TorrentProvider):
info = tr.find('a', href=rc['info'])
title = (info.attrs.get('title') or info.get_text()).strip()
download_url = self.urls['get'] % {
'id': re.sub(rc['get'], r'\1', str(info.attrs['href'])),
'title': str(title).replace(' ', '.')}
download_url = self._link('%s/%s' % (
re.sub(rc['get'], r'\1', str(info.attrs['href'])), str(title).replace(' ', '.')))
except (AttributeError, TypeError, ValueError, KeyError):
continue
@ -120,5 +124,13 @@ class SceneTimeProvider(generic.TorrentProvider):
return results
def ui_string(self, key):
if 'scenetime_digest' == key and self._valid_home():
current_url = getattr(self, 'urls', {}).get('config_provider_home_uri')
return ('use... \'uid=xx; pass=yy\'' +
(current_url and (' from a session logged in at <a target="_blank" href="%s">%s</a>' %
(anon_url(current_url), current_url.strip('/'))) or ''))
return ''
provider = SceneTimeProvider()

View file

@ -51,8 +51,7 @@ class ShazbatProvider(generic.TorrentProvider):
return super(ShazbatProvider, self)._authorised(
logged_in=(lambda y=None: '<input type="password"' not in helpers.getURL(
self.urls['feeds'], session=self.session)),
post_params={'tv_login': self.username, 'tv_password': self.password, 'form_tmpl': True})
self.urls['feeds'], session=self.session)), post_params={'tv_login': self.username, 'form_tmpl': True})
def _search_provider(self, search_params, **kwargs):
@ -107,7 +106,7 @@ class ShazbatProvider(generic.TorrentProvider):
if self._peers_fail(mode, seeders, leechers):
continue
sizes = [(tryInt(x[0], x[0]), tryInt(x[1], False)) for x in
re.findall('([\d\.]+\w+)?(?:\s*[\(\[](\d+)[\)\]])?', stats) if x[0]][0]
re.findall('([\d.]+\w+)?(?:\s*[(\[](\d+)[)\]])?', stats) if x[0]][0]
size = sizes[(0, 1)[1 < len(sizes)]]
for element in [x for x in tr.find_all('td')[2].contents[::-1] if unicode(x).strip()]:

View file

@ -35,7 +35,7 @@ class TorrentDayProvider(generic.TorrentProvider):
self.url_tmpl = {'config_provider_home_uri': '%(home)s', 'login': '%(home)s%(vars)s',
'search': '%(home)s%(vars)s', 'get': '%(home)s%(vars)s'}
self.categories = {'Season': [31, 33, 14], 'Episode': [24, 32, 26, 7, 34, 2], 'Anime': [29]}
self.categories = {'Season': [31, 33, 14], 'Episode': [24, 32, 26, 7, 34, 2], 'anime': [29]}
self.categories['Cache'] = self.categories['Season'] + self.categories['Episode']
self.proper_search_terms = None

View file

@ -5060,6 +5060,12 @@ class ConfigProviders(Config):
if hasattr(torrent_src, attr):
setattr(torrent_src, attr, str(kwargs.get(src_id_prefix + attr, '')).strip() or 'eponly')
attr = 'filter'
if hasattr(torrent_src, attr):
setattr(torrent_src, attr,
[k for k in torrent_src.may_filter.keys()
if config.checkbox_to_value(kwargs.get('%sfilter_%s' % (src_id_prefix, k)))])
# update nzb source settings
for nzb_src in [src for src in sickbeard.providers.sortedProviderList() if
sickbeard.GenericProvider.NZB == src.providerType]: