diff --git a/CHANGES.md b/CHANGES.md index 3edcf20..fee23aa 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -22,6 +22,15 @@ +### 0.18.20 (2019-04-23 23:10:00 UTC) + +* Add NinjaCentral usenet provider +* Remove Nzb.org usenet provider (r.i.p) +* Remove Milkie torrent provider (last activity > 3 months) +* Fix setting ignore/require words in webapi +* Change handle TVDb api returns None for some shows as 'seriesName' + + ### 0.18.19 (2019-04-19 02:00:00 UTC) * Fix season search at provider ETTV diff --git a/gui/slick/images/providers/ninjacentral.png b/gui/slick/images/providers/ninjacentral.png new file mode 100644 index 0000000..c4861ff Binary files /dev/null and b/gui/slick/images/providers/ninjacentral.png differ diff --git a/lib/tvdb_api/tvdb_api.py b/lib/tvdb_api/tvdb_api.py index 4bb7ebf..2a4fb64 100644 --- a/lib/tvdb_api/tvdb_api.py +++ b/lib/tvdb_api/tvdb_api.py @@ -659,6 +659,14 @@ class Tvdb: v = '' else: v = clean_data(v) + else: + if 'seriesname' == k: + if isinstance(data.get('aliases'), list) and 0 < len(data.get('aliases')): + v = data['aliases'].pop(0) + # this is a invalid show, it has no Name + if None is v: + return None + if k in map_show: k = map_show[k] if k_org is not k: @@ -677,9 +685,13 @@ class Tvdb: if isinstance(resp['data'], dict): resp['data'] = map_show_keys(resp['data']) elif isinstance(resp['data'], list): + data_list = [] for idx, row in enumerate(resp['data']): if isinstance(row, dict): - resp['data'][idx] = map_show_keys(row) + cr = map_show_keys(row) + if None is not cr: + data_list.append(cr) + resp['data'] = data_list return resp return dict([(u'data', None)]) diff --git a/sickbeard/config.py b/sickbeard/config.py index 507ddba..5d98408 100644 --- a/sickbeard/config.py +++ b/sickbeard/config.py @@ -647,10 +647,9 @@ class ConfigMigrator: if name == 'Sick Beard Index': key = '0' - if name == 'NZBs.org': - cat_ids = '5030,5040,5060,5070,5090' - else: - cat_ids = '5030,5040,5060' + cat_ids = '5030,5040,5060' + # if name == 'NZBs.org': + # cat_ids = '5030,5040,5060,5070,5090' cur_provider_data_list = [name, url, key, cat_ids, enabled] new_newznab_data.append('|'.join(cur_provider_data_list)) diff --git a/sickbeard/providers/__init__.py b/sickbeard/providers/__init__.py index b612c3c..a0ca7b5 100755 --- a/sickbeard/providers/__init__.py +++ b/sickbeard/providers/__init__.py @@ -33,7 +33,7 @@ __all__ = [ 'alpharatio', 'bb', 'beyondhd', 'bithdtv', 'blutopia', 'btn', 'custom01', 'custom11', 'dh', 'ettv', 'eztv', 'fano', 'filelist', 'funfile', 'grabtheinfo', 'hdbits', 'hdme', 'hdspace', 'hdtorrents', 'horriblesubs', - 'immortalseed', 'iptorrents', 'limetorrents', 'magnetdl', 'milkie', 'morethan', 'nebulance', 'ncore', 'nyaa', + 'immortalseed', 'iptorrents', 'limetorrents', 'magnetdl', 'morethan', 'nebulance', 'ncore', 'nyaa', 'pisexy', 'potuk', 'pretome', 'privatehd', 'ptf', 'rarbg', 'revtt', 'scenehd', 'scenetime', 'shazbat', 'showrss', 'skytorrents', 'snowfl', 'speedcd', 'thepiratebay', 'torlock', 'torrentday', 'torrenting', 'torrentleech', 'tvchaosuk', @@ -203,8 +203,8 @@ def makeTorrentRssProvider(configString): def getDefaultNewznabProviders(): return '!!!'.join(['Sick Beard Index|https://lolo.sickbeard.com/|0|5030,5040|0|eponly|0|0|0', 'NZBgeek|https://api.nzbgeek.info/||5030,5040|0|eponly|0|0|0', - 'NZBs.org|https://nzbs.org/||5030,5040|0|eponly|0|0|0', 'DrunkenSlug|https://api.drunkenslug.com/||5030,5040|0|eponly|0|0|0', + 'NinjaCentral|https://ninjacentral.co.za/||5030,5040|0|eponly|0|0|0', ]) diff --git a/sickbeard/providers/generic.py b/sickbeard/providers/generic.py index 1f6f8f0..e5504dd 100644 --- a/sickbeard/providers/generic.py +++ b/sickbeard/providers/generic.py @@ -1261,8 +1261,8 @@ class NZBProvider(GenericProvider): return results index = 0 - alt_search = ('nzbs_org' == self.get_id()) - do_search_alt = False + # alt_search = ('nzbs_org' == self.get_id()) + # do_search_alt = False search_terms = [] regex = [] @@ -1282,16 +1282,17 @@ class NZBProvider(GenericProvider): break search_params = {'q': search_terms[index], 'maxage': sickbeard.BACKLOG_DAYS + 2} - if alt_search: - - if do_search_alt: - search_params['t'] = 'search' - index += 1 - - do_search_alt = not do_search_alt - - else: - index += 1 + # if alt_search: + # + # if do_search_alt: + # search_params['t'] = 'search' + # index += 1 + # + # do_search_alt = not do_search_alt + # + # else: + # index += 1 + index += 1 for item in self._search_provider({'Propers': [search_params]}): diff --git a/sickbeard/providers/milkie.py b/sickbeard/providers/milkie.py deleted file mode 100644 index c20083f..0000000 --- a/sickbeard/providers/milkie.py +++ /dev/null @@ -1,85 +0,0 @@ -# coding=utf-8 -# -# Author: SickGear -# -# 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 . - -from . import generic -from sickbeard.helpers import tryInt -from lib.unidecode import unidecode - - -class MilkieProvider(generic.TorrentProvider): - - def __init__(self): - - generic.TorrentProvider.__init__(self, 'Milkie') - - self.url_base = 'https://milkie.cc/' - - self.api = self.url_base + 'api/v1/' - self.urls = {'config_provider_home_uri': self.url_base, - 'login': self.api + 'auth/sessions', 'get': self.api + 'torrents/%s', - 'search': self.api + 'torrents?pi=0&ps=100&query=%s&categories=2&mode=release'} - - self.username, self.password, self.minseed, self.minleech, self._token = 5 * [None] - - def _authorised(self, **kwargs): - - return super(MilkieProvider, self)._authorised( - post_params=dict(login=False), post_json=dict(email=self.username, password=self.password), - json=True, logged_in=self.logged_in) - - def logged_in(self, resp=None): - - self._token = resp and resp.get('token') - return bool(self._token) - - def _search_provider(self, search_params, **kwargs): - - results = [] - if not self._authorised(): - return results - - items = {'Cache': [], 'Season': [], 'Episode': [], 'Propers': []} - - 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_url = self.urls['search'] % search_string - - data_json = self.get_url(search_url, headers=dict(Authorization='Bearer %s' % self._token), json=True) - if self.should_skip(): - return results - - cnt = len(items[mode]) - if data_json: - for tr in data_json.get('releases'): - seeders, leechers, size = (tryInt(n, n) for n in [ - tr.get(x) for x in ('seeders', 'leechers', 'size')]) - if not self._reject_item(seeders, leechers): - title, download_url = tr.get('releaseName'), self._link(tr.get('shortId')) - if title and download_url: - items[mode].append((title, download_url, seeders, self._bytesizer(size))) - - self._log_search(mode, len(items[mode]) - cnt, search_url) - - results = self._sort_seeding(mode, results + items[mode]) - - return results - - -provider = MilkieProvider() diff --git a/sickbeard/providers/newznab.py b/sickbeard/providers/newznab.py index 75433ca..c3c985d 100755 --- a/sickbeard/providers/newznab.py +++ b/sickbeard/providers/newznab.py @@ -317,11 +317,11 @@ class NewznabProvider(generic.NZBProvider): caps[NewznabConstants.SEARCH_TEXT] = 'q' if NewznabConstants.CAT_HD not in cats or not cats.get(NewznabConstants.CAT_HD): - cats[NewznabConstants.CAT_HD] = (['5040'], ['5040', '5090'])['nzbs_org' == self.get_id()] + cats[NewznabConstants.CAT_HD] = ['5040'] # (['5040'], ['5040', '5090'])['nzbs_org' == self.get_id()] if NewznabConstants.CAT_SD not in cats or not cats.get(NewznabConstants.CAT_SD): - cats[NewznabConstants.CAT_SD] = (['5030'], ['5030', '5070'])['nzbs_org' == self.get_id()] + cats[NewznabConstants.CAT_SD] = ['5030'] # (['5030'], ['5030', '5070'])['nzbs_org' == self.get_id()] if NewznabConstants.CAT_ANIME not in cats or not cats.get(NewznabConstants.CAT_ANIME): - cats[NewznabConstants.CAT_ANIME] = (['5070'], ['6070', '7040'])['nzbs_org' == self.get_id()] + cats[NewznabConstants.CAT_ANIME] = ['5070'] # (['5070'], ['6070', '7040'])['nzbs_org' == self.get_id()] if NewznabConstants.CAT_SPORT not in cats or not cats.get(NewznabConstants.CAT_SPORT): cats[NewznabConstants.CAT_SPORT] = ['5060'] @@ -763,7 +763,8 @@ class NewznabProvider(generic.NZBProvider): base_params_uc['t'] = base_params['cat'] request_params = base_params.copy() - if ('Propers' == mode or 'nzbs_org' == self.get_id()) \ + # if ('Propers' == mode or 'nzbs_org' == self.get_id()) \ + if 'Propers' == mode \ and 'q' in params and not (any(x in params for x in ['season', 'ep'])): request_params['t'] = 'search' request_params.update(params) @@ -892,8 +893,8 @@ class NewznabProvider(generic.NZBProvider): return results index = 0 - alt_search = ('nzbs_org' == self.get_id()) - do_search_alt = False + # alt_search = ('nzbs_org' == self.get_id()) + # do_search_alt = False search_terms = [] regex = [] @@ -913,16 +914,17 @@ class NewznabProvider(generic.NZBProvider): break search_params = {'q': search_terms[index], 'maxage': sickbeard.BACKLOG_DAYS + 2} - if alt_search: - - if do_search_alt: - search_params['t'] = 'search' - index += 1 - - do_search_alt = not do_search_alt - - else: - index += 1 + # if alt_search: + # + # if do_search_alt: + # search_params['t'] = 'search' + # index += 1 + # + # do_search_alt = not do_search_alt + # + # else: + # index += 1 + index += 1 items, n_space = self._search_provider({'Propers': [search_params]}) diff --git a/sickbeard/webapi.py b/sickbeard/webapi.py index 89a8bc7..0e58daf 100644 --- a/sickbeard/webapi.py +++ b/sickbeard/webapi.py @@ -92,6 +92,13 @@ quality_map = {'sdtv': Quality.SDTV, quality_map_inversed = {v: k for k, v in quality_map.iteritems()} +class PythonObjectEncoder(json.JSONEncoder): + def default(self, obj): + if isinstance(obj, set): + return list(obj) + return json.JSONEncoder.default(self, obj) + + class Api(webserve.BaseHandler): """ api class that returns json results """ version = 10 # use an int since float-point is unpredictible @@ -182,7 +189,7 @@ class Api(webserve.BaseHandler): def _out_as_json(self, dict): self.set_header('Content-Type', 'application/json; charset=UTF-8') try: - out = json.dumps(dict, indent=self.intent, sort_keys=True) + out = json.dumps(dict, indent=self.intent, sort_keys=True, cls=PythonObjectEncoder) callback = self.get_query_argument('callback', None) or self.get_query_argument('jsonp', None) if None is not callback: out = '%s(%s);' % (callback, out) # wrap with JSONP call if requested @@ -2684,7 +2691,7 @@ class CMD_SickGearListIgnoreWords(ApiCall): return_type = 'Global' return_data['use regex'] = ignore_words.startswith('regex:') - return_data['ignore words'] = [w.strip() for w in ignore_words.replace('regex:', '').split(',')] + return_data['ignore words'] = [w.strip() for w in ignore_words.replace('regex:', '').split(',') if w.strip()] return _responds(RESULT_SUCCESS, data=return_data, msg="%s ignore words" % return_type) @@ -2717,20 +2724,22 @@ class CMD_SickGearSetIgnoreWords(ApiCall): def _create_ignore_words(): use_regex = ignore_words.startswith('regex:') - ignore_list = [w.strip() for w in ignore_words.replace('regex:', '').split(',')] + ignore_list = {w.strip() for w in ignore_words.replace('regex:', '').split(',') if w.strip()} if None is not self.regex: use_regex = self.regex if self.add: for a in self.add: - ignore_list.append(a) + ignore_list.add(a.strip()) if self.remove: for r in self.remove: try: ignore_list.remove(r) - except ValueError: + except KeyError: pass - return use_regex, ignore_list, '%s%s' % (('', 'regex:')[use_regex], ', '.join(ignore_list)) + return use_regex, ignore_list, \ + ('', '%s%s' % (('', 'regex:')[use_regex], + ', '.join([i.strip() for i in ignore_list if i.strip()])))[0 < len(ignore_list)] if self.indexer and self.indexerid: showObj = helpers.find_show_by_id(sickbeard.showList, {self.indexer: self.indexerid}) @@ -2804,7 +2813,7 @@ class CMD_SickGearListRequireWords(ApiCall): return_type = 'Global' return_data['use regex'] = required_words.startswith('regex:') - return_data['required words'] = [w.strip() for w in required_words.replace('regex:', '').split(',')] + return_data['required words'] = [w.strip() for w in required_words.replace('regex:', '').split(',') if w.strip()] return _responds(RESULT_SUCCESS, data=return_data, msg="%s required words" % return_type) @@ -2831,26 +2840,28 @@ class CMD_SickGearSetRequrieWords(ApiCall): ApiCall.__init__(self, handler, args, kwargs) def run(self): - """ set ignore words """ + """ set require words """ if not self.add and not self.remove: return _responds(RESULT_FAILURE, msg="No words to add/remove provided") def _create_required_words(): use_regex = requried_words.startswith('regex:') - require_list = [w.strip() for w in requried_words.replace('regex:', '').split(',')] + require_list = {w.strip() for w in requried_words.replace('regex:', '').split(',') if w.strip()} if None is not self.regex: use_regex = self.regex if self.add: for a in self.add: - require_list.append(a) + require_list.add(a.strip()) if self.remove: for r in self.remove: try: require_list.remove(r) - except ValueError: + except KeyError: pass - return use_regex, require_list, '%s%s' % (('', 'regex:')[use_regex], ', '.join(require_list)) + return use_regex, require_list, \ + ('', '%s%s' % (('', 'regex:')[use_regex], + ', '.join([r.strip() for r in require_list if r.strip()])))[0 < len(require_list)] if self.indexer and self.indexerid: showObj = helpers.find_show_by_id(sickbeard.showList, {self.indexer: self.indexerid}) diff --git a/tests/test_lib.py b/tests/test_lib.py index e1eca26..9a92277 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -86,7 +86,7 @@ sickbeard.NAMING_MULTI_EP = 1 sickbeard.PROVIDER_ORDER = ['sick_beard_index'] -sickbeard.newznabProviderList = providers.getNewznabProviderList("'Sick Beard Index|http://lolo.sickbeard.com/|0|5030,5040,5060|0|eponly|0!!!NZBs.org|https://nzbs.org/||5030,5040,5060,5070,5090|0|eponly|0!!!Usenet-Crawler|https://www.usenet-crawler.com/||5030,5040,5060|0|eponly|0'") +sickbeard.newznabProviderList = providers.getNewznabProviderList("'Sick Beard Index|http://lolo.sickbeard.com/|0|5030,5040,5060|0|eponly|0'") sickbeard.providerList = providers.makeProviderList() sickbeard.PROG_DIR = os.path.abspath('..')