diff --git a/gui/slick/images/providers/t411.png b/gui/slick/images/providers/t411.png new file mode 100644 index 00000000..cc41c5ed Binary files /dev/null and b/gui/slick/images/providers/t411.png differ 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/__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/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 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.+?)((?[^- ]+([. _-]\[.*\])?))?)?$ '''), 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 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) diff --git a/sickbeard/providers/__init__.py b/sickbeard/providers/__init__.py index e4b97e7d..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 @@ -186,7 +187,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): 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/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): diff --git a/sickbeard/providers/newznab.py b/sickbeard/providers/newznab.py index d4686b52..92e4e1c6 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, @@ -86,6 +89,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 = [] @@ -239,6 +288,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 +309,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 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() diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py index f05bf557..58d7435f 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,24 +2030,25 @@ 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: @@ -2029,19 +2058,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) @@ -2196,21 +2225,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 +2267,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