From dcf8ac7adae6f8127ede279afe407ecf4a1da590 Mon Sep 17 00:00:00 2001 From: Ruud Date: Sat, 20 Aug 2011 16:29:51 +0200 Subject: [PATCH] Multi Newznab provider support --- couchpotato/core/providers/base.py | 49 ++++--- .../core/providers/nzb/newznab/__init__.py | 7 +- .../core/providers/nzb/newznab/main.py | 43 ++++-- .../providers/nzb/newznab/static/newznab.js | 137 ++++++++++++++++++ couchpotato/static/scripts/page/settings.js | 4 +- 5 files changed, 200 insertions(+), 40 deletions(-) create mode 100644 couchpotato/core/providers/nzb/newznab/static/newznab.js diff --git a/couchpotato/core/providers/base.py b/couchpotato/core/providers/base.py index b5d8d298..4f986a7b 100644 --- a/couchpotato/core/providers/base.py +++ b/couchpotato/core/providers/base.py @@ -2,10 +2,12 @@ from couchpotato.core.logger import CPLog from couchpotato.core.plugins.base import Plugin from couchpotato.environment import Env from urllib2 import URLError +from urlparse import urlparse import math import re import socket import time +import urllib import urllib2 log = CPLog(__name__) @@ -16,9 +18,9 @@ class Provider(Plugin): type = None # movie, nzb, torrent, subtitle, trailer time_between_searches = 10 # Default timeout for url requests - last_use = 0 - last_available_check = 0 - is_available = 0 + last_use = {} + last_available_check = {} + is_available = {} def getCache(self, cache_key): cache = Env.get('cache').get(cache_key) @@ -35,49 +37,48 @@ class Provider(Plugin): if Env.get('debug'): return True now = time.time() + host = urlparse(test_url).hostname - if self.last_available_check < now - 900: - self.last_available_check = now + if self.last_available_check.get(host) < now - 900: + self.last_available_check[host] = now try: self.urlopen(test_url, 30) - self.is_available = True + self.is_available[host] = True except (IOError, URLError): log.error('%s unavailable, trying again in an 15 minutes.' % self.name) - self.is_available = False + self.is_available[host] = False - return self.is_available + return self.is_available[host] - def urlopen(self, url, timeout = 10, username = None, password = None): + def urlopen(self, url, timeout = 10, params = {}): socket.setdefaulttimeout(timeout) - self.wait() + + host = urlparse(url).hostname + self.wait(host) try: - log.info('Opening url: %s' % url) - if username and password: - passman = urllib2.HTTPPasswordMgrWithDefaultRealm() - passman.add_password(None, url, username, password) - authhandler = urllib2.HTTPBasicAuthHandler(passman) - opener = urllib2.build_opener(authhandler) - data = opener.open(url).read() - else: - data = urllib2.urlopen(url).read() - + log.info('Opening url: %s, params: %s' % (url, params)) + request = urllib2.Request(url, urllib.urlencode(params)) + data = urllib2.urlopen(request).read() except IOError, e: log.error('Failed opening url, %s: %s' % (url, e)) data = '' - self.last_use = time.time() + self.last_use[host] = time.time() return data - def wait(self): + def wait(self, host = ''): now = time.time() - wait = math.ceil(self.last_use - now + self.time_between_searches) + + last_use = self.last_use.get(host, 0) + + wait = math.ceil(last_use - now + self.time_between_searches) if wait > 0: log.debug('Waiting for %s, %d seconds' % (self.getName(), wait)) - time.sleep(self.last_use - now + self.time_between_searches) + time.sleep(last_use - now + self.time_between_searches) class MovieProvider(Provider): diff --git a/couchpotato/core/providers/nzb/newznab/__init__.py b/couchpotato/core/providers/nzb/newznab/__init__.py index 7674c972..8af1643a 100644 --- a/couchpotato/core/providers/nzb/newznab/__init__.py +++ b/couchpotato/core/providers/nzb/newznab/__init__.py @@ -9,11 +9,16 @@ config = [{ { 'tab': 'providers', 'name': 'newznab', + 'description': 'Enable multiple NewzNab providers', 'options': [ { 'name': 'enabled', 'type': 'enabler', }, + { + 'name': 'use', + 'default': '0' + }, { 'name': 'host', 'default': 'http://nzb.su', @@ -23,7 +28,7 @@ config = [{ 'name': 'api_key', 'default': '', 'label': 'Api Key', - 'description': 'Can be found after login on the "API" page, bottom left. The string after "&apikey=".', + 'description': 'Can be found on your profile page', }, ], }, diff --git a/couchpotato/core/providers/nzb/newznab/main.py b/couchpotato/core/providers/nzb/newznab/main.py index 6f86fac7..cb5e63a1 100644 --- a/couchpotato/core/providers/nzb/newznab/main.py +++ b/couchpotato/core/providers/nzb/newznab/main.py @@ -35,26 +35,41 @@ class Newznab(NZBProvider, RSS): addEvent('provider.nzb.search', self.search) addEvent('provider.yarr.search', self.search) - def getUrl(self, type): - return cleanHost(self.conf('host')) + 'api?t=' + type + self.registerStatic(__file__) + + def getUrl(self, host, type): + return cleanHost(host) + 'api?t=' + type def search(self, movie, quality): + uses = str(self.conf('use')).split(',') + hosts = self.conf('host').split(',') + api_keys = self.conf('api_key').split(',') + + for nr in range(len(hosts)): + self.singleSearch({ + 'use': uses[nr], + 'host': hosts[nr], + 'api_key': api_keys[nr] + }, movie, quality) + + def singleSearch(self, host, movie, quality): + results = [] - if self.isDisabled() or not self.isAvailable(self.getUrl(self.urls['search'])): + if self.isDisabled(host) or not self.isAvailable(self.getUrl(host['host'], self.urls['search'])): return results cat_id = self.getCatId(quality['identifier']) arguments = urlencode({ 'imdbid': movie['library']['identifier'].replace('tt', ''), 'cat': cat_id[0], - 'apikey': self.conf('api_key'), + 'apikey': host['api_key'], 't': self.urls['search'], 'extended': 1 }) - url = "%s&%s" % (self.getUrl(self.urls['search']), arguments) + url = "%s&%s" % (self.getUrl(host['host'], self.urls['search']), arguments) - cache_key = 'newznab.%s.%s' % (movie['library']['identifier'], cat_id[0]) + cache_key = 'newznab.%s.%s.%s' % (host['host'], movie['library']['identifier'], cat_id[0]) single_cat = (len(cat_id) == 1 and cat_id[0] != self.cat_backup_id) try: @@ -91,8 +106,8 @@ class Newznab(NZBProvider, RSS): 'name': self.getTextElement(nzb, "title"), 'age': self.calculateAge(int(time.mktime(parse(date).timetuple()))), 'size': int(size) / 1024 / 1024, - 'url': (self.getUrl(self.urls['download']) % id) + self.getApiExt(), - 'detail_url': (self.getUrl(self.urls['detail']) % id) + self.getApiExt(), + 'url': (self.getUrl(host['host'], self.urls['download']) % id) + self.getApiExt(host), + 'detail_url': (self.getUrl(host['host'], self.urls['detail']) % id) + self.getApiExt(host), 'content': self.getTextElement(nzb, "description"), } new['score'] = fireEvent('score.calculate', new, movie, single = True) @@ -112,11 +127,11 @@ class Newznab(NZBProvider, RSS): return results - def isEnabled(self): - return NZBProvider.isEnabled(self) and self.conf('host') and self.conf('api_key') + def isDisabled(self, host): + return not self.isEnabled(host) - def getApiExt(self): - return '&apikey=%s' % self.conf('api_key') + def isEnabled(self, host): + return NZBProvider.isEnabled(self) and host['host'] and host['api_key'] and int(host['use']) - def detailLink(self, id): - return self.getUrl(self.detailUrl) % id + def getApiExt(self, host): + return '&apikey=%s' % host['api_key'] diff --git a/couchpotato/core/providers/nzb/newznab/static/newznab.js b/couchpotato/core/providers/nzb/newznab/static/newznab.js new file mode 100644 index 00000000..61d9dc12 --- /dev/null +++ b/couchpotato/core/providers/nzb/newznab/static/newznab.js @@ -0,0 +1,137 @@ +var MultipleNewznab = new Class({ + + input_types: ['use', 'host', 'api_key'], + + initialize: function(){ + var self = this; + + self.items = []; + self.values = []; + self.inputs = {}; + + App.addEvent('load', self.addSettings.bind(self)) + + }, + + addSettings: function(){ + var self = this; + + self.settings = App.getPage('Settings') + self.settings.addEvent('create', function(){ + + self.fieldset = self.settings.tabs.providers.content.getElement('.section_newznab'); + + self.input_types.each(function(name){ + self.inputs[name] = self.fieldset.getElement('input[name=newznab['+name+']]'); + var values = self.inputs[name].get('value').split(','); + + values.each(function(value, nr){ + if (!self.values[nr]) self.values[nr] = {}; + self.values[nr][name] = value.trim(); + }); + + self.inputs[name].getParent().hide() + }); + + + self.values.each(function(item, nr){ + self.createItem(item.use, item.host, item.api_key); + }); + + new Element('a.nice_button', { + 'text': 'Add new NewzNab provider', + 'events': { + 'click': function(e){ + (e).stop(); + + self.createItem(1, '', ''); + } + } + }).inject(self.fieldset.getElement('h2'), 'after'); + + }) + + }, + + createItem: function(use, host, api){ + var self = this; + + var checkbox = new Element('input[type=checkbox].use.inlay', { + 'checked': +use, + 'events': { + 'click': self.save.bind(self), + 'change': self.save.bind(self) + } + }) + + var item = new Element('div.ctrlHolder').adopt( + checkbox, + new Element('input[type=text].host.inlay', { + 'value': host, + 'placeholder': 'Host', + 'events': { + 'keyup': self.save.bind(self), + 'change': self.save.bind(self) + } + }), + new Element('input[type=text].api_key.inlay', { + 'value': api, + 'placeholder': 'Api hash key', + 'events': { + 'keyup': self.save.bind(self), + 'change': self.save.bind(self) + } + }), + new Element('a.icon.delete', { + 'text': 'delete', + 'events': { + 'click': self.deleteItem.bind(self) + } + }) + ).inject(self.fieldset); + + new Form.Check(checkbox, { + 'onChange': checkbox.fireEvent.bind(checkbox, 'change') + }); + + self.items.include(item) + + }, + + save: function(){ + var self = this; + + var temp = {} + self.items.each(function(item, nr){ + self.input_types.each(function(type){ + var input = item.getElement('input.'+type); + + if(!temp[type]) temp[type] = []; + temp[type][nr] = input.get('type') == 'checkbox' ? +input.get('checked') : input.get('value').trim(); + + }) + }); + + self.input_types.each(function(name){ + self.inputs[name].set('value', temp[name].join(',')); + self.inputs[name].fireEvent('change'); + }); + + + }, + + deleteItem: function(e){ + var self = this; + (e).stop(); + + var item = e.target.getParent(); + + self.items.erase(item); + item.destroy(); + + self.save(); + } + +}); + +window.MNewznab = new MultipleNewznab(); diff --git a/couchpotato/static/scripts/page/settings.js b/couchpotato/static/scripts/page/settings.js index c1e92239..8a43d525 100644 --- a/couchpotato/static/scripts/page/settings.js +++ b/couchpotato/static/scripts/page/settings.js @@ -121,7 +121,9 @@ Page.Settings = new Class({ // Create the group if(!self.tabs[group.tab].groups[group.name]){ - var group_el = self.createGroup(group).inject(self.tabs[group.tab].content); + var group_el = self.createGroup(group) + .inject(self.tabs[group.tab].content) + .addClass('section_'+section_name); self.tabs[group.tab].groups[group.name] = group_el }