Multi Newznab provider support
This commit is contained in:
1 parent
96822fb5a0
commit
dcf8ac7ada
5 files changed
+200
-40
No files matched your search
@@ -2,10 +2,12 @@ from couchpotato.core.logger import CPLog
|
|||||||
from couchpotato.core.plugins.base import Plugin
|
from couchpotato.core.plugins.base import Plugin
|
||||||
from couchpotato.environment import Env
|
from couchpotato.environment import Env
|
||||||
from urllib2 import URLError
|
from urllib2 import URLError
|
||||||
|
from urlparse import urlparse
|
||||||
import math
|
import math
|
||||||
import re
|
import re
|
||||||
import socket
|
import socket
|
||||||
import time
|
import time
|
||||||
|
import urllib
|
||||||
import urllib2
|
import urllib2
|
||||||
|
|
||||||
log = CPLog(__name__)
|
log = CPLog(__name__)
|
||||||
@@ -16,9 +18,9 @@ class Provider(Plugin):
|
|||||||
type = None # movie, nzb, torrent, subtitle, trailer
|
type = None # movie, nzb, torrent, subtitle, trailer
|
||||||
time_between_searches = 10 # Default timeout for url requests
|
time_between_searches = 10 # Default timeout for url requests
|
||||||
|
|
||||||
last_use = 0
|
last_use = {}
|
||||||
last_available_check = 0
|
last_available_check = {}
|
||||||
is_available = 0
|
is_available = {}
|
||||||
|
|
||||||
def getCache(self, cache_key):
|
def getCache(self, cache_key):
|
||||||
cache = Env.get('cache').get(cache_key)
|
cache = Env.get('cache').get(cache_key)
|
||||||
@@ -35,49 +37,48 @@ class Provider(Plugin):
|
|||||||
if Env.get('debug'): return True
|
if Env.get('debug'): return True
|
||||||
|
|
||||||
now = time.time()
|
now = time.time()
|
||||||
|
host = urlparse(test_url).hostname
|
||||||
|
|
||||||
if self.last_available_check < now - 900:
|
if self.last_available_check.get(host) < now - 900:
|
||||||
self.last_available_check = now
|
self.last_available_check[host] = now
|
||||||
try:
|
try:
|
||||||
self.urlopen(test_url, 30)
|
self.urlopen(test_url, 30)
|
||||||
self.is_available = True
|
self.is_available[host] = True
|
||||||
except (IOError, URLError):
|
except (IOError, URLError):
|
||||||
log.error('%s unavailable, trying again in an 15 minutes.' % self.name)
|
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)
|
socket.setdefaulttimeout(timeout)
|
||||||
self.wait()
|
|
||||||
|
host = urlparse(url).hostname
|
||||||
|
self.wait(host)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
log.info('Opening url: %s' % url)
|
log.info('Opening url: %s, params: %s' % (url, params))
|
||||||
if username and password:
|
request = urllib2.Request(url, urllib.urlencode(params))
|
||||||
passman = urllib2.HTTPPasswordMgrWithDefaultRealm()
|
data = urllib2.urlopen(request).read()
|
||||||
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()
|
|
||||||
|
|
||||||
except IOError, e:
|
except IOError, e:
|
||||||
log.error('Failed opening url, %s: %s' % (url, e))
|
log.error('Failed opening url, %s: %s' % (url, e))
|
||||||
data = ''
|
data = ''
|
||||||
|
|
||||||
self.last_use = time.time()
|
self.last_use[host] = time.time()
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def wait(self):
|
def wait(self, host = ''):
|
||||||
now = time.time()
|
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:
|
if wait > 0:
|
||||||
log.debug('Waiting for %s, %d seconds' % (self.getName(), wait))
|
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):
|
class MovieProvider(Provider):
|
||||||
|
|||||||
@@ -9,11 +9,16 @@ config = [{
|
|||||||
{
|
{
|
||||||
'tab': 'providers',
|
'tab': 'providers',
|
||||||
'name': 'newznab',
|
'name': 'newznab',
|
||||||
|
'description': 'Enable multiple NewzNab providers',
|
||||||
'options': [
|
'options': [
|
||||||
{
|
{
|
||||||
'name': 'enabled',
|
'name': 'enabled',
|
||||||
'type': 'enabler',
|
'type': 'enabler',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
'name': 'use',
|
||||||
|
'default': '0'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
'name': 'host',
|
'name': 'host',
|
||||||
'default': 'http://nzb.su',
|
'default': 'http://nzb.su',
|
||||||
@@ -23,7 +28,7 @@ config = [{
|
|||||||
'name': 'api_key',
|
'name': 'api_key',
|
||||||
'default': '',
|
'default': '',
|
||||||
'label': 'Api Key',
|
'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',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -35,26 +35,41 @@ class Newznab(NZBProvider, RSS):
|
|||||||
addEvent('provider.nzb.search', self.search)
|
addEvent('provider.nzb.search', self.search)
|
||||||
addEvent('provider.yarr.search', self.search)
|
addEvent('provider.yarr.search', self.search)
|
||||||
|
|
||||||
def getUrl(self, type):
|
self.registerStatic(__file__)
|
||||||
return cleanHost(self.conf('host')) + 'api?t=' + type
|
|
||||||
|
def getUrl(self, host, type):
|
||||||
|
return cleanHost(host) + 'api?t=' + type
|
||||||
|
|
||||||
def search(self, movie, quality):
|
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 = []
|
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
|
return results
|
||||||
|
|
||||||
cat_id = self.getCatId(quality['identifier'])
|
cat_id = self.getCatId(quality['identifier'])
|
||||||
arguments = urlencode({
|
arguments = urlencode({
|
||||||
'imdbid': movie['library']['identifier'].replace('tt', ''),
|
'imdbid': movie['library']['identifier'].replace('tt', ''),
|
||||||
'cat': cat_id[0],
|
'cat': cat_id[0],
|
||||||
'apikey': self.conf('api_key'),
|
'apikey': host['api_key'],
|
||||||
't': self.urls['search'],
|
't': self.urls['search'],
|
||||||
'extended': 1
|
'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)
|
single_cat = (len(cat_id) == 1 and cat_id[0] != self.cat_backup_id)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -91,8 +106,8 @@ class Newznab(NZBProvider, RSS):
|
|||||||
'name': self.getTextElement(nzb, "title"),
|
'name': self.getTextElement(nzb, "title"),
|
||||||
'age': self.calculateAge(int(time.mktime(parse(date).timetuple()))),
|
'age': self.calculateAge(int(time.mktime(parse(date).timetuple()))),
|
||||||
'size': int(size) / 1024 / 1024,
|
'size': int(size) / 1024 / 1024,
|
||||||
'url': (self.getUrl(self.urls['download']) % id) + self.getApiExt(),
|
'url': (self.getUrl(host['host'], self.urls['download']) % id) + self.getApiExt(host),
|
||||||
'detail_url': (self.getUrl(self.urls['detail']) % id) + self.getApiExt(),
|
'detail_url': (self.getUrl(host['host'], self.urls['detail']) % id) + self.getApiExt(host),
|
||||||
'content': self.getTextElement(nzb, "description"),
|
'content': self.getTextElement(nzb, "description"),
|
||||||
}
|
}
|
||||||
new['score'] = fireEvent('score.calculate', new, movie, single = True)
|
new['score'] = fireEvent('score.calculate', new, movie, single = True)
|
||||||
@@ -112,11 +127,11 @@ class Newznab(NZBProvider, RSS):
|
|||||||
|
|
||||||
return results
|
return results
|
||||||
|
|
||||||
def isEnabled(self):
|
def isDisabled(self, host):
|
||||||
return NZBProvider.isEnabled(self) and self.conf('host') and self.conf('api_key')
|
return not self.isEnabled(host)
|
||||||
|
|
||||||
def getApiExt(self):
|
def isEnabled(self, host):
|
||||||
return '&apikey=%s' % self.conf('api_key')
|
return NZBProvider.isEnabled(self) and host['host'] and host['api_key'] and int(host['use'])
|
||||||
|
|
||||||
def detailLink(self, id):
|
def getApiExt(self, host):
|
||||||
return self.getUrl(self.detailUrl) % id
|
return '&apikey=%s' % host['api_key']
|
||||||
@@ -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();
|
||||||
@@ -121,7 +121,9 @@ Page.Settings = new Class({
|
|||||||
|
|
||||||
// Create the group
|
// Create the group
|
||||||
if(!self.tabs[group.tab].groups[group.name]){
|
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
|
self.tabs[group.tab].groups[group.name] = group_el
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user