From b2941d6bc027ea00385e603e28405640bd00addd Mon Sep 17 00:00:00 2001 From: dhellwich Date: Sat, 30 Aug 2014 16:17:09 +0200 Subject: [PATCH 01/12] Do not log ERROR when show isn't in list. Log WARNING instead. Prevents the error log in webinterface from filling up, if there are downloads in your watch folder, which aren't in SB list --- sickbeard/postProcessor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sickbeard/postProcessor.py b/sickbeard/postProcessor.py index 544efe78..9e749782 100644 --- a/sickbeard/postProcessor.py +++ b/sickbeard/postProcessor.py @@ -793,7 +793,7 @@ class PostProcessor(object): (show, season, episodes, quality, version) = self._find_info() if not show: self._log(u"This show isn't in your list, you need to add it to SB before post-processing an episode", - logger.ERROR) + logger.WARNING) raise exceptions.PostProcessingFailed() elif season == None or not episodes: self._log(u"Not enough information to determine what episode this is", logger.DEBUG) From ecab1c35f83c5574aeb7b28070c61830f8c5424d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=84lveborn?= Date: Sun, 31 Aug 2014 15:16:13 +0200 Subject: [PATCH 02/12] Fixed missing slash on Kat mirror URL Changed http://www.kickmirror.com to http://www.kickmirror.com/ --- sickbeard/providers/kat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sickbeard/providers/kat.py b/sickbeard/providers/kat.py index 5bea6248..d3e3aa95 100644 --- a/sickbeard/providers/kat.py +++ b/sickbeard/providers/kat.py @@ -61,7 +61,7 @@ class KATProvider(generic.TorrentProvider): self.cache = KATCache(self) - self.urls = ['http://kickass.to/', 'http://katproxy.com/', 'http://www.kickmirror.com'] + self.urls = ['http://kickass.to/', 'http://katproxy.com/', 'http://www.kickmirror.com/'] self.url = None def isEnabled(self): From 8cdf1ddbee5d8e4d90673b48dc2672697e239f07 Mon Sep 17 00:00:00 2001 From: Julio Melendez Date: Sun, 31 Aug 2014 11:10:00 -0500 Subject: [PATCH 03/12] Switch from urlencode to json Per the Pushbullet API (https://docs.pushbullet.com/http/) the POST should use a JSON body, with content-type 'application/json' --- sickbeard/notifiers/pushbullet.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sickbeard/notifiers/pushbullet.py b/sickbeard/notifiers/pushbullet.py index 8a731e1a..274d9715 100644 --- a/sickbeard/notifiers/pushbullet.py +++ b/sickbeard/notifiers/pushbullet.py @@ -20,7 +20,7 @@ import base64 from httplib import HTTPSConnection, HTTPException -from urllib import urlencode +import json from ssl import SSLError import sickbeard from sickbeard import logger, common @@ -97,8 +97,9 @@ class PushbulletNotifier: 'body': message.encode('utf-8'), 'device_iden': pushbullet_device, 'type': notificationType} - http_handler.request(method, uri, body=urlencode(data), - headers={'Authorization': 'Basic %s' % authString}) + data = json.dumps(data) + http_handler.request(method, uri, body=data, + headers={'Content-Type': 'application/json', 'Authorization': 'Basic %s' % authString}) pass except (SSLError, HTTPException): return False From 90356544cdf4f94f120acc71ece3b8bd1c780a0f Mon Sep 17 00:00:00 2001 From: echel0n Date: Mon, 1 Sep 2014 00:04:30 -0700 Subject: [PATCH 04/12] Sports regex updated --- sickbeard/name_parser/regexes.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sickbeard/name_parser/regexes.py b/sickbeard/name_parser/regexes.py index ba30f72b..4de242bf 100644 --- a/sickbeard/name_parser/regexes.py +++ b/sickbeard/name_parser/regexes.py @@ -88,7 +88,7 @@ normal_regexes = [ # Show Name - 2010-11-23 - Ep Name ''' ^((?P.+?)[. _-]+)? # Show_Name and separator - (?P(\d{4}[. _-]+\d{1,2}[. _-]+\d{1,2})|(\d{1,2}\w{2}[. _-]+\w+[. _-]+\d{4})|(\w+[. _-]+\d{1,2}\w{2}[. _-]+\d{4})) + (?P(\d+[. _-]\d+[. _-]\d+)|(\d+\w+[. _-]\w+[. _-]\d+))[. _-]+ [. _-]*((?P.+?) # Source_Quality_Etc- ((?[^- ]+([. _-]\[.*\])?))?)?$ # Group @@ -100,8 +100,8 @@ normal_regexes = [ # Show Name - 2010-11-23 - Ep Name ''' ^(?P.*?(UEFA|MLB|ESPN|WWE|MMA|UFC|TNA|EPL|NASCAR|NBA|NFL|NHL|NRL|PGA|SUPER LEAGUE|FORMULA|FIFA|NETBALL|MOTOGP).*?)[. _-]+ - ((?P\d{1,3}).*?)? - (?P(\d{2}[. _-]+\d{2}[. _-]+\d{2})|(\d{4}[. _-]+\d{1,2}[. _-]+\d{1,2})|(\d{1,2}\w{2}[. _-]+\w+[. _-]+\d{4})|(\w+[. _-]+\d{1,2}\w{2}[. _-]+\d{4}))[. _-]* + ((?P\d{1,3})[. _-]+)? + (?P(\d+[. _-]\d+[. _-]\d+)|(\d+\w+[. _-]\w+[. _-]\d+))[. _-]+ ((?P.+?)((?[^- ]+([. _-]\[.*\])?))?)?$ '''), From 000467cc73c38cca10c061f85f44acdb938a5af6 Mon Sep 17 00:00:00 2001 From: Woodpaker Date: Mon, 1 Sep 2014 17:57:52 +0200 Subject: [PATCH 05/12] * Added saving of changed newznab categories in backend. Added gui for selecting categories from multiselect box. Created some helper function in js, for dynamically modifying selects/options Made results of function for retrieving newznab capabilities more generic. In that now always a valid json is returned with success,tv_categories,error Added gui elements for retrieving and displaying newznab capabilities Added backend functions for calling ajax /getNewznabCategories?name=yourNewznabProvider&url=https://newznabprovURL&key=YourApiKey Returns json.dumps() with TV category capabilities of newznab provider. Is going to be used for new gui element in adding newsnab provider. --- .../interfaces/default/config_providers.tmpl | 20 +- gui/slick/js/configProviders.js | 191 +++++++++++++++++- sickbeard/providers/newznab.py | 51 ++++- sickbeard/webserve.py | 56 ++--- 4 files changed, 283 insertions(+), 35 deletions(-) diff --git a/gui/slick/interfaces/default/config_providers.tmpl b/gui/slick/interfaces/default/config_providers.tmpl index b0aae4eb..97081ee6 100644 --- a/gui/slick/interfaces/default/config_providers.tmpl +++ b/gui/slick/interfaces/default/config_providers.tmpl @@ -22,7 +22,7 @@ \$(document).ready(function(){ var show_nzb_providers = #if $sickbeard.USE_NZBS then "true" else "false"#; #for $curNewznabProvider in $sickbeard.newznabProviderList: -\$(this).addProvider('$curNewznabProvider.getID()', '$curNewznabProvider.name', '$curNewznabProvider.url', '$curNewznabProvider.key', $int($curNewznabProvider.default), show_nzb_providers); +\$(this).addProvider('$curNewznabProvider.getID()', '$curNewznabProvider.name', '$curNewznabProvider.url', '$curNewznabProvider.key', '$curNewznabProvider.catIDs', $int($curNewznabProvider.default), show_nzb_providers); #end for }); //--> @@ -572,6 +572,24 @@ var show_nzb_providers = #if $sickbeard.USE_NZBS then "true" else "false"#; (if not required, type 0) + +
+ + + +
+
diff --git a/gui/slick/js/configProviders.js b/gui/slick/js/configProviders.js index 52eb6a39..56fd8c93 100644 --- a/gui/slick/js/configProviders.js +++ b/gui/slick/js/configProviders.js @@ -13,7 +13,41 @@ $(document).ready(function(){ }); } - $.fn.addProvider = function (id, name, url, key, isDefault, showProvider) { + + $.fn.getCategories = function (isDefault, name, url, key) { + + if (!name) + return; + + if (!url) + return; + + if (!key) + return; + + var params = {url: url, name: name, key: key}; + var returnData; + + $.ajaxSetup( { "async": false } ); + $.getJSON(sbRoot + '/config/providers/getNewznabCategories', params, + function(data){ + if (data.error != "") { + alert(data.error); + return false; + } + + if (data.success == false) { + return false; + } + + console.debug(data.tv_categories); + returnData = data; + }); + $.ajaxSetup( { "async": true } ); + return returnData; + } + + $.fn.addProvider = function (id, name, url, key, cat, isDefault, showProvider) { url = $.trim(url); if (!url) @@ -25,7 +59,7 @@ $(document).ready(function(){ if (url.match('/$') == null) url = url + '/'; - var newData = [isDefault, [name, url, key]]; + var newData = [isDefault, [name, url, key, cat]]; newznabProviders[id] = newData; if (!isDefault){ @@ -63,10 +97,11 @@ $(document).ready(function(){ } - $.fn.updateProvider = function (id, url, key) { + $.fn.updateProvider = function (id, url, key, cat) { newznabProviders[id][1][1] = url; newznabProviders[id][1][2] = key; + newznabProviders[id][1][3] = cat; $(this).populateNewznabSection(); @@ -108,17 +143,49 @@ $(document).ready(function(){ var isDefault = 0; $('#newznab_add_div').show(); $('#newznab_update_div').hide(); + $('#newznab_cat').attr('disabled','disabled'); + $('#newznab_cap').attr('disabled','disabled'); + + $("#newznab_cat option").each(function() { + $(this).remove(); + return; + }); + + $("#newznab_cap option").each(function() { + $(this).remove(); + return; + }); + } else { var data = newznabProviders[selectedProvider][1]; var isDefault = newznabProviders[selectedProvider][0]; $('#newznab_add_div').hide(); $('#newznab_update_div').show(); + $('#newznab_cat').removeAttr("disabled"); + $('#newznab_cap').removeAttr("disabled"); } $('#newznab_name').val(data[0]); $('#newznab_url').val(data[1]); $('#newznab_key').val(data[2]); - + + //Check if not already array + if (typeof data[3] === 'string') { + rrcat = data[3].split(",") + } + else { + rrcat = data[3]; + } + + // Update the category select box (on the right) + var newCatOptions = []; + if (rrcat) { + rrcat.forEach(function (cat) { + newCatOptions.push({text : cat, value : cat}); + }); + $("#newznab_cat").replaceOptions(newCatOptions); + }; + if (selectedProvider == 'addNewznab') { $('#newznab_name').removeAttr("disabled"); $('#newznab_url').removeAttr("disabled"); @@ -132,11 +199,51 @@ $(document).ready(function(){ } else { $('#newznab_url').removeAttr("disabled"); $('#newznab_delete').removeAttr("disabled"); + + //Get Categories Capabilities + if (data[0] && data[1] && data[2] && !ifExists($.fn.newznabProvidersCapabilities, data[0])) { + var categoryresult = $(this).getCategories(isDefault, data[0], data[1], data[2]); + if (categoryresult && categoryresult.success && categoryresult.tv_categories) { + $.fn.newznabProvidersCapabilities.push({'name' : data[0], 'categories' : categoryresult.tv_categories}); + } + + } + + //Loop through the array and if currently selected newznab provider name matches one in the array, use it to + //update the capabilities select box (on the left). + if (data[0]) { + $.fn.newznabProvidersCapabilities.forEach(function(newzNabCap) { + + if (newzNabCap.name && newzNabCap.name == data[0] && newzNabCap.categories instanceof Array) { + var newCapOptions = []; + newzNabCap.categories.forEach(function(category_set) { + if (category_set.id && category_set.name) { + newCapOptions.push({value : category_set.id, text : category_set.name + "(" + category_set.id + ")"}); + }; + }); + $("#newznab_cap").replaceOptions(newCapOptions); + } + + }); + }; + } } } + ifExists = function(loopThroughArray, searchFor) { + var found = false; + + loopThroughArray.forEach(function(rootObject) { + if (rootObject.name == searchFor) { + found = true; + } + console.log(rootObject.name + " while searching for: "+ searchFor); + }); + return found; + }; + $.fn.makeNewznabProviderString = function() { var provStrings = new Array(); @@ -294,9 +401,10 @@ $(document).ready(function(){ provider_id = provider_id.substring(0, provider_id.length-'_hash'.length); var url = $('#'+provider_id+'_url').val(); + var cat = $('#'+provider_id+'_cat').val(); var key = $(this).val(); - $(this).updateProvider(provider_id, url, key); + $(this).updateProvider(provider_id, url, key, cat); }); @@ -310,7 +418,11 @@ $(document).ready(function(){ var url = $('#newznab_url').val(); var key = $('#newznab_key').val(); - $(this).updateProvider(selectedProvider, url, key); + var cat = $('#newznab_cat option').map(function(i, opt) { + return $(opt).text(); + }).toArray().join(','); + + $(this).updateProvider(selectedProvider, url, key, cat); }); @@ -344,6 +456,48 @@ $(document).ready(function(){ $(this).refreshProviderList(); }); + $(this).on('click', '#newznab_cat_update', function(){ + console.debug('Clicked Button'); + + //Maybe check if there is anything selected? + $("#newznab_cat option").each(function() { + $(this).remove(); + return; + }); + + var newOptions = []; + + // When the update botton is clicked, loop through the capabilities list + // and copy the selected category id's to the category list on the right. + $("#newznab_cap option").each(function(){ + if($(this).attr('selected') == 'selected') + { + var selected_cat = $(this).val(); + console.debug(selected_cat); + newOptions.push({text: selected_cat, value: selected_cat}) + }; + }); + + $("#newznab_cat").replaceOptions(newOptions); + + var selectedProvider = $('#editANewznabProvider :selected').val(); + if (selectedProvider == "addNewznab") + return; + + var url = $('#newznab_url').val(); + var key = $('#newznab_key').val(); + + var cat = $('#newznab_cat option').map(function(i, opt) { + return $(opt).text(); + }).toArray().join(','); + + $("#newznab_cat option:not([value])").remove(); + + $(this).updateProvider(selectedProvider, url, key, cat); + + }); + + $('#newznab_add').click(function(){ var selectedProvider = $('#editANewznabProvider :selected').val(); @@ -351,6 +505,11 @@ $(document).ready(function(){ var name = $.trim($('#newznab_name').val()); var url = $.trim($('#newznab_url').val()); var key = $.trim($('#newznab_key').val()); + //var cat = $.trim($('#newznab_cat').val()); + + var cat = $.trim($('#newznab_cat option').map(function(i, opt) { + return $(opt).text();}).toArray().join(',')); + if (!name) return; @@ -371,7 +530,7 @@ $(document).ready(function(){ return; } - $(this).addProvider(data.success, name, url, key, 0); + $(this).addProvider(data.success, name, url, key, cat, 0); }); }); @@ -465,9 +624,27 @@ $(document).ready(function(){ $(this).makeTorrentOptionString(provider_id); }); + + + $.fn.replaceOptions = function(options) { + var self, $option; + + this.empty(); + self = this; + + $.each(options, function(index, option) { + $option = $("") + .attr("value", option.value) + .text(option.text); + self.append($option); + }); + }; // initialization stuff + + $.fn.newznabProvidersCapabilities = []; + $(this).hideConfigTab(); $(this).showHideProviders(); diff --git a/sickbeard/providers/newznab.py b/sickbeard/providers/newznab.py index d4686b52..10f57251 100755 --- a/sickbeard/providers/newznab.py +++ b/sickbeard/providers/newznab.py @@ -37,6 +37,9 @@ from sickbeard import logger from sickbeard import tvcache from sickbeard.exceptions import ex, AuthException +from lib import requests +from lib.requests import exceptions +from lib.bencode import bdecode class NewznabProvider(generic.NZBProvider): def __init__(self, name, url, key='', catIDs='5030,5040', search_mode='eponly', search_fallback=False, @@ -52,8 +55,6 @@ class NewznabProvider(generic.NZBProvider): self.search_mode = search_mode self.search_fallback = search_fallback - self.enable_daily = enable_daily - self.enable_backlog = enable_backlog # a 0 in the key spot indicates that no key is needed if self.key == '0': @@ -86,6 +87,52 @@ class NewznabProvider(generic.NZBProvider): def isEnabled(self): return self.enabled + def _getURL(self, url, post_data=None, params=None, timeout=30, json=False): + """ + By default this is just a simple urlopen call but this method should be overridden + for providers with special URL requirements (like cookies) + Not really changed much from the superclass, can be used in future. + """ + + # check for auth + if not self._doLogin(): + return + + return helpers.getURL(url, post_data=post_data, params=params, headers=self.headers, timeout=timeout, + session=self.session, json=json) + + def get_newznab_categories(self): + """ + Uses the newznab provider url and apikey to get the capabilities. + Makes use of the default newznab caps param. e.a. http://yournewznab/api?t=caps&apikey=skdfiw7823sdkdsfjsfk + Returns a tuple with (succes or not, array with dicts [{"id": "5070", "name": "Anime"}, + {"id": "5080", "name": "Documentary"}, {"id": "5020", "name": "Foreign"}...etc}], error message) + """ + return_categories = [] + + self._checkAuth() + + params = {"t": "caps"} + if self.needs_auth and self.key: + params['apikey'] = self.key + + categories = self.getURL("%s/api" % (self.url), params=params) + + xml_categories = helpers.parse_xml(categories) + + if not xml_categories: + return (False, return_categories, "Error parsing xml for [%s]" % (self.name)) + + try: + for category in xml_categories.iter('category'): + if category.get('name') == 'TV': + for subcat in category.findall('subcat'): + return_categories.append(subcat.attrib) + except: + return (False, return_categories, "Error parsing result for [%s]" % (self.name)) + + return (True, return_categories, "") + def _get_season_search_strings(self, ep_obj): to_return = [] diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py index f05bf557..9b15db9a 100644 --- a/sickbeard/webserve.py +++ b/sickbeard/webserve.py @@ -1909,6 +1909,34 @@ class ConfigProviders(MainHandler): sickbeard.newznabProviderList.append(newProvider) return newProvider.getID() + '|' + newProvider.configStr() + def getNewznabCategories(self, name, url, key): + ''' + Retrieves a list of possible categories with category id's + Using the default url/api?cat + http://yournewznaburl.com/api?t=caps&apikey=yourapikey + ''' + error = "" + success = False + + if not name: + error += "\nNo Provider Name specified" + if not url: + error += "\nNo Provider Url specified" + if not key: + error += "\nNo Provider Api key specified" + + if error <> "": + return json.dumps({'success' : False, 'error': error}) + + #Get list with Newznabproviders + #providerDict = dict(zip([x.getID() for x in sickbeard.newznabProviderList], sickbeard.newznabProviderList)) + + #Get newznabprovider obj with provided name + tempProvider= newznab.NewznabProvider(name, url, key) + + success, tv_categories, error = tempProvider.get_newznab_categories() + + return json.dumps({'success' : success,'tv_categories' : tv_categories, 'error' : error}) def deleteNewznabProvider(self, nnid): @@ -2002,46 +2030,24 @@ class ConfigProviders(MainHandler): if not curNewznabProviderStr: continue - cur_name, cur_url, cur_key = curNewznabProviderStr.split('|') + cur_name, cur_url, cur_key, cur_cat = curNewznabProviderStr.split('|') cur_url = config.clean_url(cur_url) newProvider = newznab.NewznabProvider(cur_name, cur_url, key=cur_key) + cur_id = newProvider.getID() # if it already exists then update it if cur_id in newznabProviderDict: newznabProviderDict[cur_id].name = cur_name newznabProviderDict[cur_id].url = cur_url - newznabProviderDict[cur_id].key = cur_key + newznabProviderDict[cur_id].catIDs = cur_cat # a 0 in the key spot indicates that no key is needed if cur_key == '0': newznabProviderDict[cur_id].needs_auth = False else: newznabProviderDict[cur_id].needs_auth = True - - try: - newznabProviderDict[cur_id].search_mode = str(kwargs[cur_id + '_search_mode']).strip() - except: - pass - - try: - newznabProviderDict[cur_id].search_fallback = config.checkbox_to_value( - kwargs[cur_id + '_search_fallback']) - except: - pass - - try: - newznabProviderDict[cur_id].enable_daily = config.checkbox_to_value( - kwargs[cur_id + '_enable_daily']) - except: - pass - - try: - newznabProviderDict[cur_id].enable_backlog = config.checkbox_to_value( - kwargs[cur_id + '_enable_backlog']) - except: - pass else: sickbeard.newznabProviderList.append(newProvider) From 8b4bb3a5a5f50c89aec1003cbe56b71795e06b0b Mon Sep 17 00:00:00 2001 From: Woodpaker Date: Mon, 1 Sep 2014 18:29:52 +0200 Subject: [PATCH 06/12] Fixed some git conflicts --- sickbeard/providers/newznab.py | 2 ++ sickbeard/webserve.py | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/sickbeard/providers/newznab.py b/sickbeard/providers/newznab.py index 10f57251..f9bf9b68 100755 --- a/sickbeard/providers/newznab.py +++ b/sickbeard/providers/newznab.py @@ -55,6 +55,8 @@ class NewznabProvider(generic.NZBProvider): self.search_mode = search_mode self.search_fallback = search_fallback + self.enable_daily = enable_daily + self.enable_backlog = enable_backlog # a 0 in the key spot indicates that no key is needed if self.key == '0': diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py index 9b15db9a..22c88d15 100644 --- a/sickbeard/webserve.py +++ b/sickbeard/webserve.py @@ -2048,6 +2048,29 @@ class ConfigProviders(MainHandler): newznabProviderDict[cur_id].needs_auth = False else: newznabProviderDict[cur_id].needs_auth = True + + try: + newznabProviderDict[cur_id].search_mode = str(kwargs[cur_id + '_search_mode']).strip() + except: + pass + + try: + newznabProviderDict[cur_id].search_fallback = config.checkbox_to_value( + kwargs[cur_id + '_search_fallback']) + except: + pass + + try: + newznabProviderDict[cur_id].enable_daily = config.checkbox_to_value( + kwargs[cur_id + '_enable_daily']) + except: + pass + + try: + newznabProviderDict[cur_id].enable_backlog = config.checkbox_to_value( + kwargs[cur_id + '_enable_backlog']) + except: + pass else: sickbeard.newznabProviderList.append(newProvider) From 20725c1da73da7abe7fe5a062cde4a33c68e7c4f Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 3 Sep 2014 15:36:42 +0800 Subject: [PATCH 07/12] Testing fix for missing api key on newznab providers --- sickbeard/providers/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sickbeard/providers/__init__.py b/sickbeard/providers/__init__.py index e4b97e7d..7fb02eda 100755 --- a/sickbeard/providers/__init__.py +++ b/sickbeard/providers/__init__.py @@ -186,7 +186,7 @@ def makeTorrentRssProvider(configString): def getDefaultNewznabProviders(): - return 'Sick Beard Index|http://lolo.sickbeard.com/|0|5030,5040|0|eponly|0|0|0!!!NZBs.org|https://nzbs.org/|0|5030,5040|0|eponly|0|0|0!!!Usenet-Crawler|https://www.usenet-crawler.com/|0|5030,5040|0|eponly|0|0|0' + return 'Sick Beard Index|http://lolo.sickbeard.com/|0|5030,5040|0|eponly|0|0|0!!!NZBs.org|https://nzbs.org/||5030,5040|0|eponly|0|0|0!!!Usenet-Crawler|https://www.usenet-crawler.com/||5030,5040|0|eponly|0|0|0' def getProviderModule(name): From 281247a90fe4b20bbde319ec1dbc1a8b7639af6d Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 3 Sep 2014 18:33:26 +0800 Subject: [PATCH 08/12] Fix for daily/backlog checkbox values not saving correctly on providers --- sickbeard/webserve.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py index f05bf557..57e7ba9e 100644 --- a/sickbeard/webserve.py +++ b/sickbeard/webserve.py @@ -2196,21 +2196,21 @@ class ConfigProviders(MainHandler): curTorrentProvider.search_fallback = config.checkbox_to_value( kwargs[curTorrentProvider.getID() + '_search_fallback']) except: - curTorrentProvider.search_fallback = 0 + curTorrentProvider.search_fallback = 0 # these exceptions are catching unselected checkboxes if hasattr(curTorrentProvider, 'enable_daily'): try: curTorrentProvider.enable_daily = config.checkbox_to_value( kwargs[curTorrentProvider.getID() + '_enable_daily']) except: - curTorrentProvider.enable_daily = 1 + curTorrentProvider.enable_daily = 0 # these exceptions are actually catching unselected checkboxes if hasattr(curTorrentProvider, 'enable_backlog'): try: curTorrentProvider.enable_backlog = config.checkbox_to_value( kwargs[curTorrentProvider.getID() + '_enable_backlog']) except: - curTorrentProvider.enable_backlog = 1 + curTorrentProvider.enable_backlog = 0 # these exceptions are actually catching unselected checkboxes for curNzbProvider in [curProvider for curProvider in sickbeard.providers.sortedProviderList() if curProvider.providerType == sickbeard.GenericProvider.NZB]: @@ -2238,21 +2238,21 @@ class ConfigProviders(MainHandler): curNzbProvider.search_fallback = config.checkbox_to_value( kwargs[curNzbProvider.getID() + '_search_fallback']) except: - curNzbProvider.search_fallback = 0 + curNzbProvider.search_fallback = 0 # these exceptions are actually catching unselected checkboxes if hasattr(curNzbProvider, 'enable_daily'): try: curNzbProvider.enable_daily = config.checkbox_to_value( kwargs[curNzbProvider.getID() + '_enable_daily']) except: - curNzbProvider.enable_daily = 1 + curNzbProvider.enable_daily = 0 # these exceptions are actually catching unselected checkboxes if hasattr(curNzbProvider, 'enable_backlog'): try: curNzbProvider.enable_backlog = config.checkbox_to_value( kwargs[curNzbProvider.getID() + '_enable_backlog']) except: - curNzbProvider.enable_backlog = 1 + curNzbProvider.enable_backlog = 0 # these exceptions are actually catching unselected checkboxes sickbeard.NEWZNAB_DATA = '!!!'.join([x.configStr() for x in sickbeard.newznabProviderList]) sickbeard.PROVIDER_ORDER = provider_list From 0daf22631347cdd6f4e337a3a2c75732292c3ecc Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 4 Sep 2014 07:52:39 +0800 Subject: [PATCH 09/12] Test #2 to fix daily/backlog checkboxes with custom newznab server --- sickbeard/webserve.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py index 57e7ba9e..deec5eef 100644 --- a/sickbeard/webserve.py +++ b/sickbeard/webserve.py @@ -2029,19 +2029,19 @@ class ConfigProviders(MainHandler): newznabProviderDict[cur_id].search_fallback = config.checkbox_to_value( kwargs[cur_id + '_search_fallback']) except: - pass + newznabProviderDict[cur_id].search_fallback = 0 try: newznabProviderDict[cur_id].enable_daily = config.checkbox_to_value( kwargs[cur_id + '_enable_daily']) except: - pass + newznabProviderDict[cur_id].enable_daily = 0 try: newznabProviderDict[cur_id].enable_backlog = config.checkbox_to_value( kwargs[cur_id + '_enable_backlog']) except: - pass + newznabProviderDict[cur_id].enable_backlog = 0 else: sickbeard.newznabProviderList.append(newProvider) From 1317848782be1ad348de73c7016e413974f72c90 Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 5 Sep 2014 18:40:05 +0800 Subject: [PATCH 10/12] Fix infinite loop with dognzb --- sickbeard/providers/newznab.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/sickbeard/providers/newznab.py b/sickbeard/providers/newznab.py index d4686b52..2042bab9 100755 --- a/sickbeard/providers/newznab.py +++ b/sickbeard/providers/newznab.py @@ -239,6 +239,18 @@ class NewznabProvider(generic.NZBProvider): except (AttributeError, TypeError): break + # sanity check - limiting at 10 at getting 1000 results in-case incorrect total parameter is reported + if params['limit'] > 1000: + logger.log("Excessive results for search, ending search", logger.WARNING) + break + + # sanity check - total should remain constant + if offset != 0 and total != initial_total: + logger.log("Total number of items on newznab response changed, ending search", logger.DEBUG) + break + else: + initial_total = total + # if there are more items available then the amount given in one call, grab some more if (total - params['limit']) > offset == params['offset']: params['offset'] += params['limit'] @@ -248,9 +260,7 @@ class NewznabProvider(generic.NZBProvider): else: break - # sanity check - limiting at 10 at getting 1000 results in-case incorrect total parameter is reported - if params['limit'] > 1000: - break + else: break From f944604f79af87a7eaac1f703aee63d8267a369d Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 5 Sep 2014 22:02:16 +0800 Subject: [PATCH 11/12] Fix #3 for daily/backlog checkbox saving --- sickbeard/config.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sickbeard/config.py b/sickbeard/config.py index 9b98d2f3..ff3afbd7 100644 --- a/sickbeard/config.py +++ b/sickbeard/config.py @@ -250,6 +250,10 @@ def checkbox_to_value(option, value_on=1, value_off=0): Turns checkbox option 'on' or 'true' to value_on (1) any other value returns value_off (0) """ + + if type(option) is list: + option = option[-1] + if option == 'on' or option == 'true': return value_on From 159e8ceb7c712b49e553d31fed567339c8b37211 Mon Sep 17 00:00:00 2001 From: djoole Date: Sat, 6 Sep 2014 02:16:04 +0200 Subject: [PATCH 12/12] Added the torrent provider for www.t411.me tracker --- gui/slick/images/providers/t411.png | Bin 0 -> 6957 bytes sickbeard/__init__.py | 2 +- sickbeard/providers/__init__.py | 3 +- sickbeard/providers/generic.py | 7 +- sickbeard/providers/t411.py | 294 ++++++++++++++++++++++++++++ 5 files changed, 302 insertions(+), 4 deletions(-) create mode 100644 gui/slick/images/providers/t411.png create mode 100644 sickbeard/providers/t411.py diff --git a/gui/slick/images/providers/t411.png b/gui/slick/images/providers/t411.png new file mode 100644 index 0000000000000000000000000000000000000000..cc41c5edce68f2c5b626c151f540f900cad6e890 GIT binary patch literal 6957 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 Lg+i*8t)KlD*SWF~ literal 0 HcmV?d00001 diff --git a/sickbeard/__init__.py b/sickbeard/__init__.py index 88248e48..e5771b0f 100755 --- a/sickbeard/__init__.py +++ b/sickbeard/__init__.py @@ -32,7 +32,7 @@ from sickbeard import providers, metadata, config, webserveInit from sickbeard.providers.generic import GenericProvider from providers import ezrss, tvtorrents, btn, newznab, womble, thepiratebay, torrentleech, kat, iptorrents, \ omgwtfnzbs, scc, hdtorrents, torrentday, hdbits, nextgen, speedcd, nyaatorrents, fanzub, torrentbytes, animezb, \ - freshontv, bitsoup + freshontv, bitsoup, t411 from sickbeard.config import CheckSection, check_setting_int, check_setting_str, check_setting_float, ConfigMigrator, \ naming_ep_type from sickbeard import searchBacklog, showUpdater, versionChecker, properFinder, autoPostProcesser, \ diff --git a/sickbeard/providers/__init__.py b/sickbeard/providers/__init__.py index 7fb02eda..c1800ca1 100755 --- a/sickbeard/providers/__init__.py +++ b/sickbeard/providers/__init__.py @@ -36,7 +36,8 @@ __all__ = ['ezrss', 'torrentbytes', 'animezb', 'freshontv', - 'bitsoup' + 'bitsoup', + 't411' ] import sickbeard diff --git a/sickbeard/providers/generic.py b/sickbeard/providers/generic.py index 95bdc169..07131109 100644 --- a/sickbeard/providers/generic.py +++ b/sickbeard/providers/generic.py @@ -27,7 +27,7 @@ import sickbeard import requests from sickbeard import helpers, classes, logger, db -from sickbeard.common import MULTI_EP_RESULT, SEASON_RESULT +from sickbeard.common import MULTI_EP_RESULT, SEASON_RESULT, USER_AGENT from sickbeard import tvcache from sickbeard import encodingKludge as ek from sickbeard.exceptions import ex @@ -63,7 +63,10 @@ class GenericProvider: self.session = requests.session() self.headers = { - 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.107 Safari/537.36'} + #Using USER_AGENT instead of Mozilla to keep same user agent along authentication and download phases, + #otherwise session might be broken and download fail, asking again for authentication + #'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.107 Safari/537.36'} + 'User-Agent': USER_AGENT} def getID(self): return GenericProvider.makeID(self.name) diff --git a/sickbeard/providers/t411.py b/sickbeard/providers/t411.py new file mode 100644 index 00000000..45b3100a --- /dev/null +++ b/sickbeard/providers/t411.py @@ -0,0 +1,294 @@ +# -*- coding: latin-1 -*- +# Author: djoole +# URL: http://code.google.com/p/sickbeard/ +# +# This file is part of Sick Beard. +# +# Sick Beard 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. +# +# Sick Beard 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 Sick Beard. If not, see . + +import traceback +import time +import re +import datetime +import sickbeard +import generic +from lib import requests +from sickbeard.common import USER_AGENT, Quality, cpu_presets +from sickbeard import logger +from sickbeard import tvcache +from sickbeard import show_name_helpers +from sickbeard.bs4_parser import BS4Parser + +class T411Provider(generic.TorrentProvider): + urls = {'base_url': 'http://www.t411.me/', + 'search': 'http://www.t411.me/torrents/search/?name=%s&cat=210&subcat=433&search=%s&submit=Recherche', + 'login_page': 'http://www.t411.me/users/login/', + 'download': 'http://www.t411.me/torrents/download/?id=%s', + } + + def __init__(self): + + generic.TorrentProvider.__init__(self, "T411") + + self.supportsBacklog = True + + self.enabled = False + self.username = None + self.password = None + self.ratio = None + + self.cache = T411Cache(self) + + self.url = self.urls['base_url'] + + self.last_login_check = None + + self.login_opener = None + + def isEnabled(self): + return self.enabled + + def imageName(self): + return 't411.png' + + def getQuality(self, item, anime=False): + + quality = Quality.sceneQuality(item[0], anime) + return quality + + def getLoginParams(self): + return { + 'login': self.username, + 'password': self.password, + 'remember': '1', + } + + def loginSuccess(self, output): + if "Ratio: 0: + for result in entries: + + try: + link = result.find('a', title=True) + torrentName = link['title'] + torrent_name = str(torrentName) + torrentId = result.find_all('td')[2].find_all('a')[0]['href'][1:].replace('torrents/nfo/?id=','') + torrent_download_url = (self.urls['download'] % torrentId).encode('utf8') + except (AttributeError, TypeError): + continue + + if not torrent_name or not torrent_download_url: + continue + + item = torrent_name, torrent_download_url + logger.log(u"Found result: " + torrent_name + " (" + torrent_download_url + ")", logger.DEBUG) + items[mode].append(item) + + else: + logger.log(u"The Data returned from " + self.name + " do not contains any torrent", + logger.WARNING) + continue + + except Exception, e: + logger.log(u"Failed parsing " + self.name + " Traceback: " + traceback.format_exc(), + logger.ERROR) + + results += items[mode] + + return results + + def _get_title_and_url(self, item): + + title, url = item + + if title: + title = u'' + title + title = title.replace(' ', '.') + + if url: + url = str(url).replace('&', '&') + + return title, url + + def findPropers(self, search_date=datetime.datetime.today()): + + results = [] + + myDB = db.DBConnection() + sqlResults = myDB.select( + 'SELECT s.show_name, e.showid, e.season, e.episode, e.status, e.airdate FROM tv_episodes AS e' + + ' INNER JOIN tv_shows AS s ON (e.showid = s.indexer_id)' + + ' WHERE e.airdate >= ' + str(search_date.toordinal()) + + ' AND (e.status IN (' + ','.join([str(x) for x in Quality.DOWNLOADED]) + ')' + + ' OR (e.status IN (' + ','.join([str(x) for x in Quality.SNATCHED]) + ')))' + ) + + if not sqlResults: + return [] + + for sqlshow in sqlResults: + self.show = helpers.findCertainShow(sickbeard.showList, int(sqlshow["showid"])) + if self.show: + curEp = self.show.getEpisode(int(sqlshow["season"]), int(sqlshow["episode"])) + searchString = self._get_episode_search_strings(curEp, add_string='PROPER|REPACK') + + for item in self._doSearch(searchString[0]): + title, url = self._get_title_and_url(item) + results.append(classes.Proper(title, url, datetime.datetime.today(), self.show)) + + return results + + def seedRatio(self): + return self.ratio + + +class T411Cache(tvcache.TVCache): + def __init__(self, provider): + + tvcache.TVCache.__init__(self, provider) + + # Only poll T411 every 10 minutes max + self.minTime = 10 + + def _getDailyData(self): + search_params = {'RSS': ['']} + return self.provider._doSearch(search_params) + + +provider = T411Provider()