Providers, wizard base, core notifier base
This commit is contained in:
@@ -26,6 +26,8 @@ def get_session(engine = None):
|
||||
def get_engine():
|
||||
return create_engine(Env.get('db_path'), echo = False)
|
||||
|
||||
def addView(route, func, static = False):
|
||||
web.add_url_rule(route + ('' if static else '/'), endpoint = route if route else 'index', view_func = func)
|
||||
|
||||
""" Web view """
|
||||
@web.route('/')
|
||||
|
||||
+1
-1
@@ -90,7 +90,7 @@ def cmd_couchpotato(base_path, args):
|
||||
# Load configs & plugins
|
||||
loader = Env.get('loader')
|
||||
loader.preload(root = base_path)
|
||||
loader.addModule('core', 'couchpotato.core', 'core')
|
||||
loader.addModule(0, 'core', 'couchpotato.core', 'core')
|
||||
loader.run()
|
||||
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ def removeEvent(name, handler):
|
||||
e -= handler
|
||||
|
||||
def fireEvent(name, *args, **kwargs):
|
||||
log.debug('Firing "%s": %s, %s' % (name, args, kwargs))
|
||||
#log.debug('Firing "%s": %s, %s' % (name, args, kwargs))
|
||||
try:
|
||||
|
||||
# Return single handler
|
||||
@@ -64,7 +64,7 @@ def fireEvent(name, *args, **kwargs):
|
||||
log.error('%s: %s' % (name, e))
|
||||
|
||||
def fireEventAsync(name, *args, **kwargs):
|
||||
log.debug('Async "%s": %s, %s' % (name, args, kwargs))
|
||||
#log.debug('Async "%s": %s, %s' % (name, args, kwargs))
|
||||
try:
|
||||
e = events[name]
|
||||
e.asynchronous = True
|
||||
|
||||
+31
-24
@@ -18,47 +18,48 @@ class Loader:
|
||||
providers = os.path.join(root, 'couchpotato', 'core', 'providers')
|
||||
|
||||
self.paths = {
|
||||
'plugin' : ('couchpotato.core.plugins', os.path.join(core, 'plugins')),
|
||||
'notifications' : ('couchpotato.core.notifications', os.path.join(core, 'notifications')),
|
||||
'downloaders' : ('couchpotato.core.downloaders', os.path.join(root, 'couchpotato', 'core', 'downloaders')),
|
||||
'movie_provider' : ('couchpotato.core.providers.movie', os.path.join(providers, 'movie')),
|
||||
'nzb_provider' : ('couchpotato.core.providers.nzb', os.path.join(providers, 'nzb')),
|
||||
'torrent_provider' : ('couchpotato.core.providers.torrent', os.path.join(providers, 'torrent')),
|
||||
'trailer_provider' : ('couchpotato.core.providers.trailer', os.path.join(providers, 'trailer')),
|
||||
'subtitle_provider' : ('couchpotato.core.providers.subtitle', os.path.join(providers, 'subtitle')),
|
||||
'plugin' : (0, 'couchpotato.core.plugins', os.path.join(core, 'plugins')),
|
||||
'notifications' : (20, 'couchpotato.core.notifications', os.path.join(core, 'notifications')),
|
||||
'downloaders' : (20, 'couchpotato.core.downloaders', os.path.join(core, 'downloaders')),
|
||||
'movie_provider' : (20, 'couchpotato.core.providers.movie', os.path.join(providers, 'movie')),
|
||||
'nzb_provider' : (20, 'couchpotato.core.providers.nzb', os.path.join(providers, 'nzb')),
|
||||
'torrent_provider' : (20, 'couchpotato.core.providers.torrent', os.path.join(providers, 'torrent')),
|
||||
'trailer_provider' : (20, 'couchpotato.core.providers.trailer', os.path.join(providers, 'trailer')),
|
||||
'subtitle_provider' : (20, 'couchpotato.core.providers.subtitle', os.path.join(providers, 'subtitle')),
|
||||
}
|
||||
|
||||
for type, tuple in self.paths.iteritems():
|
||||
self.addFromDir(type, tuple[0], tuple[1])
|
||||
priority, module, dir = tuple
|
||||
self.addFromDir(type, priority, module, dir)
|
||||
|
||||
def run(self):
|
||||
did_save = 0
|
||||
|
||||
for module_name, plugin in sorted(self.modules.iteritems()):
|
||||
for priority in self.modules:
|
||||
for module_name, plugin in sorted(self.modules[priority].iteritems()):
|
||||
# Load module
|
||||
try:
|
||||
m = getattr(self.loadModule(module_name), plugin.get('name'))
|
||||
|
||||
# Load module
|
||||
try:
|
||||
m = getattr(self.loadModule(module_name), plugin.get('name'))
|
||||
log.info("Loading %s: %s" % (plugin['type'], plugin['name']))
|
||||
|
||||
log.info("Loading %s: %s" % (plugin['type'], plugin['name']))
|
||||
# Save default settings for plugin/provider
|
||||
did_save += self.loadSettings(m, module_name, save = False)
|
||||
|
||||
# Save default settings for plugin/provider
|
||||
did_save += self.loadSettings(m, module_name, save = False)
|
||||
|
||||
self.loadPlugins(m, plugin.get('name'))
|
||||
except Exception, e:
|
||||
log.error(e)
|
||||
self.loadPlugins(m, plugin.get('name'))
|
||||
except Exception, e:
|
||||
log.error('Can\'t import %s: %s' % (plugin.get('name'), e))
|
||||
|
||||
if did_save:
|
||||
fireEvent('settings.save')
|
||||
|
||||
def addFromDir(self, type, module, dir):
|
||||
def addFromDir(self, type, priority, module, dir):
|
||||
|
||||
for file in glob.glob(os.path.join(dir, '*')):
|
||||
name = os.path.basename(file)
|
||||
if os.path.isdir(os.path.join(dir, name)):
|
||||
module_name = '%s.%s' % (module, name)
|
||||
self.addModule(type, module_name, name)
|
||||
self.addModule(priority, type, module_name, name)
|
||||
|
||||
def loadSettings(self, module, name, save = True):
|
||||
try:
|
||||
@@ -82,8 +83,14 @@ class Loader:
|
||||
log.error("Failed loading plugin '%s': %s" % (name, e))
|
||||
return False
|
||||
|
||||
def addModule(self, type, module, name):
|
||||
self.modules[module] = {
|
||||
def addModule(self, priority, type, module, name):
|
||||
|
||||
if not self.modules.get(priority):
|
||||
self.modules[priority] = {}
|
||||
|
||||
self.modules[priority][module] = {
|
||||
'priority': priority,
|
||||
'module': module,
|
||||
'type': type,
|
||||
'name': name,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
from .main import CoreNotifier
|
||||
|
||||
def start():
|
||||
return CoreNotifier()
|
||||
|
||||
config = []
|
||||
@@ -0,0 +1,49 @@
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.event import addEvent, fireEvent
|
||||
from couchpotato.core.helpers.request import jsonified
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
import time
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
|
||||
class CoreNotifier(Plugin):
|
||||
|
||||
messages = []
|
||||
|
||||
def __init__(self):
|
||||
addEvent('notify', self.notify)
|
||||
addEvent('notify.core_notifier', self.notify)
|
||||
addEvent('core_notifier.frontend', self.frontend)
|
||||
|
||||
addApiView('core_notifier.listener', self.listener)
|
||||
|
||||
static = self.registerStatic(__file__)
|
||||
fireEvent('register_script', static + 'notification.js')
|
||||
|
||||
def notify(self, message = '', data = {}):
|
||||
self.add(data = {
|
||||
'message': message,
|
||||
'raw': data,
|
||||
})
|
||||
|
||||
def frontend(self, type = 'notification', data = {}):
|
||||
|
||||
self.messages.append({
|
||||
'time': time.time(),
|
||||
'type': type,
|
||||
'data': data,
|
||||
})
|
||||
|
||||
def listener(self):
|
||||
|
||||
for message in self.messages:
|
||||
#delete message older then 15s
|
||||
if message['time'] < (time.time() - 15):
|
||||
del message
|
||||
|
||||
return jsonified({
|
||||
'success': True,
|
||||
'result': self.messages,
|
||||
})
|
||||
@@ -0,0 +1,42 @@
|
||||
var NotificationBase = new Class({
|
||||
|
||||
Extends: BlockBase,
|
||||
Implements: [Options, Events],
|
||||
|
||||
initialize: function(options){
|
||||
var self = this;
|
||||
self.setOptions(options);
|
||||
|
||||
//App.addEvent('load', self.request.bind(self));
|
||||
|
||||
self.addEvent('notification', self.notify.bind(self))
|
||||
|
||||
},
|
||||
|
||||
request: function(){
|
||||
var self = this;
|
||||
|
||||
Api.request('core_notifier.listener', {
|
||||
'initialDelay': 100,
|
||||
'delay': 3000,
|
||||
'onComplete': self.processData.bind(self)
|
||||
}).startTimer()
|
||||
|
||||
},
|
||||
|
||||
notify: function(data){
|
||||
var self = this;
|
||||
|
||||
},
|
||||
|
||||
processData: function(json){
|
||||
var self = this;
|
||||
|
||||
Array.each(json.result, function(result){
|
||||
self.fireEvent(result.type, result.data)
|
||||
})
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
window.Notification = new NotificationBase();
|
||||
@@ -1,7 +1,8 @@
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato import addView
|
||||
from couchpotato.environment import Env
|
||||
from flask.helpers import send_from_directory
|
||||
import os.path
|
||||
import re
|
||||
|
||||
|
||||
class Plugin():
|
||||
@@ -12,20 +13,23 @@ class Plugin():
|
||||
def getName(self):
|
||||
return self.__class__.__name__
|
||||
|
||||
def registerStatic(self, file_path):
|
||||
def registerStatic(self, plugin_file):
|
||||
|
||||
class_name = self.__class__.__name__.lower()
|
||||
self.plugin_file = file_path
|
||||
path = class_name + '.static/'
|
||||
# Register plugin path
|
||||
self.plugin_path = os.path.dirname(plugin_file)
|
||||
|
||||
addApiView(path + '<path:file>', self.showStatic, static = True)
|
||||
# Get plugin_name from PluginName
|
||||
s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', self.__class__.__name__)
|
||||
class_name = re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()
|
||||
|
||||
path = 'static/' + class_name + '/'
|
||||
addView(path + '<path:file>', self.showStatic, static = True)
|
||||
|
||||
return path
|
||||
|
||||
def showStatic(self, file = ''):
|
||||
|
||||
plugin_dir = os.path.dirname(self.plugin_file)
|
||||
dir = os.path.join(plugin_dir, 'static')
|
||||
dir = os.path.join(self.plugin_path, 'static')
|
||||
|
||||
return send_from_directory(dir, file)
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
from .main import Wizard
|
||||
|
||||
def start():
|
||||
return Wizard()
|
||||
|
||||
config = [{
|
||||
'name': 'global',
|
||||
'groups': [
|
||||
{
|
||||
'tab': 'general',
|
||||
'name': 'advanced',
|
||||
'options': [
|
||||
{
|
||||
'name': 'show_wizard',
|
||||
'label': 'Run the wizard',
|
||||
'default': True,
|
||||
'type': 'bool',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}]
|
||||
@@ -0,0 +1,13 @@
|
||||
from couchpotato.core.event import fireEvent
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
|
||||
class Wizard(Plugin):
|
||||
|
||||
def __init__(self):
|
||||
path = self.registerStatic(__file__)
|
||||
fireEvent('register_script', path + 'spotlight.js')
|
||||
fireEvent('register_script', path + 'wizard.js')
|
||||
@@ -0,0 +1,306 @@
|
||||
/*
|
||||
---
|
||||
description: Fill the empty space around elements, creating a spotlight effect.
|
||||
|
||||
license: GPL v3.0
|
||||
|
||||
authors:
|
||||
- Ruud Burger
|
||||
|
||||
requires:
|
||||
- core/1.3: [Class.Extras, Element.Dimensions]
|
||||
|
||||
provides: [Spotlight]
|
||||
|
||||
...
|
||||
*/
|
||||
|
||||
var Spotlight = new Class({
|
||||
|
||||
Implements: [Options],
|
||||
|
||||
options: {
|
||||
'fillClass': 'spotlight_fill',
|
||||
'fillColor': [255,255,255],
|
||||
'fillOpacity': 1,
|
||||
'parent': null,
|
||||
'inject': null,
|
||||
'soften': 10
|
||||
},
|
||||
|
||||
initialize: function(elements, options){
|
||||
var self = this;
|
||||
self.setOptions(options);
|
||||
|
||||
self.setElements(elements);
|
||||
self.clean();
|
||||
|
||||
},
|
||||
|
||||
clean: function(){
|
||||
var self = this;
|
||||
|
||||
self.range = []; self.fills = []; self.edges = [];
|
||||
|
||||
self.vert = [];
|
||||
self.vert_el = [];
|
||||
|
||||
self.top = []; self.left = [];
|
||||
self.width = []; self.height = [];
|
||||
},
|
||||
|
||||
setElements: function(elements){
|
||||
this.elements = elements;
|
||||
},
|
||||
|
||||
addElement: function(element){
|
||||
this.elements.include(element);
|
||||
},
|
||||
|
||||
create: function(){
|
||||
var self = this;
|
||||
|
||||
self.destroy();
|
||||
|
||||
var page_c = $(self.options.parent || window).getScrollSize();
|
||||
var soften = self.options.soften;
|
||||
|
||||
// Get the top and bottom of all the elements
|
||||
self.elements.each(function(el, nr){
|
||||
var c = el.getCoordinates();
|
||||
|
||||
if(c.top > 0 && nr == 0){
|
||||
self.vert.append([0]);
|
||||
self.vert_el.append([null]);
|
||||
}
|
||||
|
||||
// Top
|
||||
self.vert.append([c.top-soften]);
|
||||
self.vert_el.append([el]);
|
||||
|
||||
// Bottom
|
||||
self.vert.append([c.top+c.height+soften]);
|
||||
self.vert_el.append([el]);
|
||||
|
||||
// Add it to range, for later calculation from left to right
|
||||
self.range.append([{
|
||||
'el': el,
|
||||
'top': c.top-soften,
|
||||
'bottom': c.top+c.height+soften,
|
||||
'left': c.left-soften,
|
||||
'right': c.left+c.width+soften
|
||||
}])
|
||||
|
||||
// Create soft edge around element
|
||||
self.soften(el);
|
||||
|
||||
});
|
||||
|
||||
if(self.elements.length == 0){
|
||||
self.vert.append([0]);
|
||||
self.vert_el.append([null]);
|
||||
}
|
||||
|
||||
// Reorder
|
||||
var vert = self.vert.clone().sort(self.numberSort) // Use custom sort function because apparently 100 is less then 20..
|
||||
vert_el_new = [], vert_new = [];
|
||||
vert.each(function(v){
|
||||
var old_nr = self.vert.indexOf(v);
|
||||
vert_el_new.append([self.vert_el[old_nr]]);
|
||||
vert_new.append([v]);
|
||||
|
||||
});
|
||||
self.vert = vert_new;
|
||||
self.vert_el = vert_el_new;
|
||||
|
||||
// Shorten vars
|
||||
var vert = self.vert,
|
||||
vert_el = self.vert_el;
|
||||
var t, h, l, w, left, width,
|
||||
row_el, cursor = 0;
|
||||
|
||||
// Loop over all vertical lines
|
||||
vert.each(function(v, nr){
|
||||
|
||||
// Use defaults if el == null (for first fillblock)
|
||||
var c = vert_el[nr] ? vert_el[nr].getCoordinates() : {
|
||||
'left': 0,
|
||||
'top': 0,
|
||||
'width': page_c.x,
|
||||
'height': 0
|
||||
};
|
||||
|
||||
// Loop till cursor gets to parent_element.width
|
||||
var fail_safe = 0;
|
||||
while (cursor < page_c.x && fail_safe < 10){
|
||||
|
||||
t = vert[nr]; // Top is the same for every element in a row
|
||||
h = (nr == vert.length-1) ? (page_c.y - t) : vert[nr+1] - vert[nr]; // So is hight
|
||||
|
||||
// First element get special treatment
|
||||
if(nr == 0){
|
||||
l = 0;
|
||||
w = c.width+(2*soften);
|
||||
cursor += w;
|
||||
}
|
||||
else {
|
||||
|
||||
row_el = self.firstFromLeft(cursor, t) // First next element
|
||||
left = row_el.el ? row_el.left : c.left-soften;
|
||||
width = row_el.el ? row_el.left - cursor : c.left-soften;
|
||||
|
||||
if(t == c.bottom+soften && !row_el.el)
|
||||
width = page_c.x;
|
||||
|
||||
l = cursor;
|
||||
if(cursor < left){
|
||||
w = width;
|
||||
cursor += w+(row_el.right - row_el.left);
|
||||
}
|
||||
else {
|
||||
w = page_c.x-l;
|
||||
cursor += w;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Add it to the pile!
|
||||
if(h > 0 && w > 0){
|
||||
self.top.append([t]); self.left.append([l]);
|
||||
self.width.append([w]); self.height.append([h]);
|
||||
}
|
||||
|
||||
fail_safe++;
|
||||
|
||||
}
|
||||
|
||||
cursor = 0; // New line, reset cursor position
|
||||
fail_safe = 0;
|
||||
|
||||
});
|
||||
|
||||
// Create the fill blocks
|
||||
self.top.each(self.createFillItem.bind(self));
|
||||
|
||||
},
|
||||
|
||||
createFillItem: function(top, nr){
|
||||
var self = this;
|
||||
|
||||
var fill = new Element('div', {
|
||||
'class': self.options.fillClass,
|
||||
'styles': {
|
||||
'position': 'absolute',
|
||||
'background-color': 'rgba('+self.options.fillColor.join(',')+', '+self.options.fillOpacity+')',
|
||||
'display': 'block',
|
||||
'z-index': 2,
|
||||
'top': self.top[nr],
|
||||
'left': self.left[nr],
|
||||
'height': self.height[nr],
|
||||
'width': self.width[nr]
|
||||
}
|
||||
}).inject(self.options.inject || document.body);
|
||||
|
||||
self.fills.include(fill);
|
||||
},
|
||||
|
||||
// Find the first element after x,y coordinates
|
||||
firstFromLeft: function(x, y){
|
||||
var self = this;
|
||||
|
||||
var lowest_left = null;
|
||||
var return_data = {};
|
||||
|
||||
self.range.each(function(range){
|
||||
var is_within_height_range = range.top <= y && range.bottom > y,
|
||||
is_within_width_range = range.left >= x,
|
||||
more_left_then_previous = range.left < lowest_left || lowest_left == null;
|
||||
|
||||
if(is_within_height_range && is_within_width_range && more_left_then_previous){
|
||||
lowest_left = range.left;
|
||||
return_data = range;
|
||||
}
|
||||
})
|
||||
|
||||
return return_data
|
||||
|
||||
},
|
||||
|
||||
soften: function(el){
|
||||
var self = this;
|
||||
var soften = self.options.soften;
|
||||
|
||||
var c = el.getCoordinates();
|
||||
var from_color = 'rgba('+self.options.fillColor.join(',')+', '+self.options.fillOpacity+')';
|
||||
var to_color = 'rgba('+self.options.fillColor.join(',')+', 0)';
|
||||
|
||||
// Top
|
||||
self.createEdge({
|
||||
'top': c.top-soften,
|
||||
'left': c.left-soften,
|
||||
'width': c.width+(2*soften),
|
||||
'background': '-webkit-gradient(linear, left top, left bottom, from('+from_color+'), to('+to_color+'))',
|
||||
'background': '-moz-linear-gradient(top, '+from_color+', '+to_color+')'
|
||||
})
|
||||
|
||||
// Right
|
||||
self.createEdge({
|
||||
'top': c.top-soften,
|
||||
'left': c.right,
|
||||
'height': c.height+(2*soften),
|
||||
'background': '-webkit-gradient(linear, left, right, from('+from_color+'), to('+to_color+'))',
|
||||
'background': '-moz-linear-gradient(right, '+from_color+', '+to_color+')'
|
||||
})
|
||||
|
||||
// Bottom
|
||||
self.createEdge({
|
||||
'top': c.bottom,
|
||||
'left': c.left-soften,
|
||||
'width': c.width+(2*soften),
|
||||
'background': '-webkit-gradient(linear, left bottom, left top, from('+from_color+'), to('+to_color+'))',
|
||||
'background': '-moz-linear-gradient(bottom, '+from_color+', '+to_color+')'
|
||||
})
|
||||
|
||||
// Left
|
||||
self.createEdge({
|
||||
'top': c.top-soften,
|
||||
'left': c.left-soften,
|
||||
'height': c.height+(2*soften),
|
||||
'background': '-webkit-gradient(linear, right, left, from('+from_color+'), to('+to_color+'))',
|
||||
'background': '-moz-linear-gradient(left, '+from_color+', '+to_color+')'
|
||||
})
|
||||
|
||||
},
|
||||
|
||||
createEdge: function(style){
|
||||
var self = this;
|
||||
|
||||
var soften = self.options.soften;
|
||||
var edge = new Element('div', {
|
||||
'styles': Object.merge({
|
||||
'position': 'absolute',
|
||||
'width': soften,
|
||||
'height': soften,
|
||||
}, style)
|
||||
}).inject(self.options.inject || document.body)
|
||||
|
||||
self.edges.include(edge);
|
||||
|
||||
},
|
||||
|
||||
destroy: function(){
|
||||
var self = this;
|
||||
self.fills.each(function(fill){
|
||||
fill.destroy();
|
||||
})
|
||||
self.edges.each(function(edge){
|
||||
edge.destroy();
|
||||
})
|
||||
self.clean();
|
||||
},
|
||||
|
||||
numberSort: function (a, b) {
|
||||
return a - b;
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,76 @@
|
||||
var WizardBase = new Class({
|
||||
|
||||
Implements: [Options, Events],
|
||||
|
||||
initialize: function(steps){
|
||||
var self = this;
|
||||
|
||||
self.steps = steps;
|
||||
self.start();
|
||||
|
||||
},
|
||||
|
||||
start: function(){
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
nextStep: function(){
|
||||
|
||||
},
|
||||
|
||||
previousStep: function(){
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
WizardBase.Screen = new Class({
|
||||
|
||||
initialize: function(data){
|
||||
var self = this;
|
||||
|
||||
self.data = data;
|
||||
self.create()
|
||||
|
||||
},
|
||||
|
||||
create: function(){
|
||||
var self = this;
|
||||
|
||||
self.el = new Element('div.')
|
||||
|
||||
|
||||
},
|
||||
|
||||
destroy: function(){
|
||||
this.el.destroy();
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
window.Wizard = new WizardBase([
|
||||
{
|
||||
'title': 'Fill in your username and password',
|
||||
'Description': 'Outside blabla',
|
||||
'tab': 'general',
|
||||
'fields': ['username', 'password']
|
||||
},
|
||||
{
|
||||
'title': 'What do you use to download your movies',
|
||||
'answers': [
|
||||
{'name': 'nzb', 'label': 'Usenet'},
|
||||
{'name': 'torrent', 'label': 'Torrents'}
|
||||
]
|
||||
},
|
||||
{
|
||||
'title': 'Do you have a login for any of the following sites',
|
||||
'tab': 'providers',
|
||||
'needs': function(){
|
||||
return self.config_nzb || self.config_torrent
|
||||
}
|
||||
}
|
||||
])
|
||||
@@ -20,6 +20,10 @@ class NZBProvider(Provider):
|
||||
time_between_searches = 10 # Seconds
|
||||
|
||||
|
||||
def isEnabled(self):
|
||||
return True # nzb_downloaded is enabled check
|
||||
|
||||
|
||||
class TorrentProvider(Provider):
|
||||
type = 'torrent'
|
||||
|
||||
|
||||
@@ -1,8 +1,138 @@
|
||||
from couchpotato.core.event import addEvent
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.providers.base import NZBProvider
|
||||
from dateutil.parser import parse
|
||||
from urllib import urlencode
|
||||
from urllib2 import URLError
|
||||
import time
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
|
||||
class Newzbin(NZBProvider):
|
||||
pass
|
||||
searchUrl = 'https://www.newzbin.com/search/'
|
||||
|
||||
formatIds = {
|
||||
2: ['scr'],
|
||||
1: ['cam'],
|
||||
4: ['tc'],
|
||||
8: ['ts'],
|
||||
1024: ['r5'],
|
||||
}
|
||||
cat_ids = [
|
||||
([2097152], ['1080p']),
|
||||
([524288], ['720p']),
|
||||
([262144], ['brrip']),
|
||||
([2], ['dvdr']),
|
||||
]
|
||||
cat_backup_id = -1
|
||||
|
||||
def __init__(self):
|
||||
addEvent('provider.nzb.search', self.search)
|
||||
|
||||
def search(self, movie, quality):
|
||||
|
||||
self.cleanCache();
|
||||
|
||||
results = []
|
||||
if not self.enabled() or not self.isAvailable(self.searchUrl):
|
||||
return results
|
||||
|
||||
formatId = self.getFormatId(type)
|
||||
catId = self.getCatId(type)
|
||||
|
||||
arguments = urlencode({
|
||||
'searchaction': 'Search',
|
||||
'u_url_posts_only': '0',
|
||||
'u_show_passworded': '0',
|
||||
'q_url': 'imdb.com/title/' + movie.imdb,
|
||||
'sort': 'ps_totalsize',
|
||||
'order': 'asc',
|
||||
'u_post_results_amt': '100',
|
||||
'feed': 'rss',
|
||||
'category': '6',
|
||||
'ps_rb_video_format': str(catId),
|
||||
'ps_rb_source': str(formatId),
|
||||
})
|
||||
|
||||
url = "%s?%s" % (self.searchUrl, arguments)
|
||||
cacheId = str('%s %s %s' % (movie.imdb, str(formatId), str(catId)))
|
||||
singleCat = True
|
||||
|
||||
try:
|
||||
cached = False
|
||||
if(self.cache.get(cacheId)):
|
||||
data = True
|
||||
cached = True
|
||||
log.info('Getting RSS from cache: %s.' % cacheId)
|
||||
else:
|
||||
log.info('Searching: %s' % url)
|
||||
data = self.urlopen(url, username = self.conf('username'), password = self.conf('password'))
|
||||
self.cache[cacheId] = {
|
||||
'time': time.time()
|
||||
}
|
||||
|
||||
except (IOError, URLError):
|
||||
log.error('Failed to open %s.' % url)
|
||||
return results
|
||||
|
||||
if data:
|
||||
try:
|
||||
try:
|
||||
if cached:
|
||||
xml = self.cache[cacheId]['xml']
|
||||
else:
|
||||
xml = self.getItems(data)
|
||||
self.cache[cacheId]['xml'] = xml
|
||||
except:
|
||||
log.debug('No valid xml or to many requests.. You never know with %s.' % self.name)
|
||||
return results
|
||||
|
||||
for item in xml:
|
||||
|
||||
title = self.gettextelement(item, "title")
|
||||
if 'error' in title.lower(): continue
|
||||
|
||||
REPORT_NS = 'http://www.newzbin.com/DTD/2007/feeds/report/';
|
||||
|
||||
# Add attributes to name
|
||||
for attr in item.find('{%s}attributes' % REPORT_NS):
|
||||
title += ' ' + attr.text
|
||||
|
||||
id = int(self.gettextelement(item, '{%s}id' % REPORT_NS))
|
||||
size = str(int(self.gettextelement(item, '{%s}size' % REPORT_NS)) / 1024 / 1024) + ' mb'
|
||||
date = str(self.gettextelement(item, '{%s}postdate' % REPORT_NS))
|
||||
|
||||
new = self.feedItem()
|
||||
new.id = id
|
||||
new.type = 'nzb'
|
||||
new.name = title
|
||||
new.date = int(time.mktime(parse(date).timetuple()))
|
||||
new.size = self.parseSize(size)
|
||||
new.url = str(self.gettextelement(item, '{%s}nzb' % REPORT_NS))
|
||||
new.detailUrl = str(self.gettextelement(item, 'link'))
|
||||
new.content = self.gettextelement(item, "description")
|
||||
new.score = self.calcScore(new, movie)
|
||||
new.addbyid = True
|
||||
new.checkNZB = False
|
||||
|
||||
if new.date > time.time() - (int(self.config.get('NZB', 'retention')) * 24 * 60 * 60) and self.isCorrectMovie(new, movie, type, imdbResults = True, singleCategory = singleCat):
|
||||
results.append(new)
|
||||
log.info('Found: %s' % new.name)
|
||||
|
||||
return results
|
||||
except SyntaxError:
|
||||
log.error('Failed to parse XML response from newzbin.com')
|
||||
|
||||
return results
|
||||
|
||||
def getFormatId(self, format):
|
||||
for id, quality in self.formatIds.iteritems():
|
||||
for q in quality:
|
||||
if q == format:
|
||||
return id
|
||||
|
||||
return self.catBackupId
|
||||
|
||||
def isEnabled(self):
|
||||
return NZBProvider.isEnabled(self) and self.conf('enabled') and self.conf('username') and self.conf('password')
|
||||
|
||||
@@ -17,6 +17,7 @@ config = [{
|
||||
{
|
||||
'name': 'host',
|
||||
'default': 'http://nzb.su',
|
||||
'description': 'The hostname of your newznab provider, like http://nzb.su'
|
||||
},
|
||||
{
|
||||
'name': 'api_key',
|
||||
|
||||
@@ -1,8 +1,126 @@
|
||||
from couchpotato.core.event import addEvent
|
||||
from couchpotato.core.helpers.variable import cleanHost
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.providers.base import NZBProvider
|
||||
from dateutil.parser import parse
|
||||
from urllib import urlencode
|
||||
from urllib2 import URLError
|
||||
import time
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
|
||||
class Newznab(NZBProvider):
|
||||
pass
|
||||
|
||||
urls = {
|
||||
'download': 'get&id=%s%s',
|
||||
'detail': 'details&id=%s',
|
||||
}
|
||||
|
||||
cat_ids = [
|
||||
([2000], ['brrip']),
|
||||
([2010], ['dvdr']),
|
||||
([2030], ['cam', 'ts', 'dvdrip', 'tc', 'r5', 'scr']),
|
||||
([2040], ['720p', '1080p']),
|
||||
]
|
||||
cat_backup_id = 2000
|
||||
|
||||
time_between_searches = 1 # Seconds
|
||||
|
||||
def __init__(self):
|
||||
addEvent('provider.nzb.search', self.search)
|
||||
|
||||
def getUrl(self, type):
|
||||
return cleanHost(self.conf('host')) + 'api?t=' + type
|
||||
|
||||
def search(self, movie, quality):
|
||||
|
||||
self.cleanCache();
|
||||
|
||||
results = []
|
||||
if not self.enabled() or not self.isAvailable(self.getUrl(self.searchUrl)):
|
||||
return results
|
||||
|
||||
catId = self.getCatId(type)
|
||||
arguments = urlencode({
|
||||
'imdbid': movie.imdb.replace('tt', ''),
|
||||
'cat': catId,
|
||||
'apikey': self.conf('apikey'),
|
||||
't': self.searchUrl,
|
||||
'extended': 1
|
||||
})
|
||||
url = "%s&%s" % (self.getUrl(self.searchUrl), arguments)
|
||||
cacheId = str(movie.imdb) + '-' + str(catId)
|
||||
singleCat = (len(self.catIds.get(catId)) == 1 and catId != self.catBackupId)
|
||||
|
||||
try:
|
||||
cached = False
|
||||
if(self.cache.get(cacheId)):
|
||||
data = True
|
||||
cached = True
|
||||
log.info('Getting RSS from cache: %s.' % cacheId)
|
||||
else:
|
||||
log.info('Searching: %s' % url)
|
||||
data = self.urlopen(url)
|
||||
self.cache[cacheId] = {
|
||||
'time': time.time()
|
||||
}
|
||||
|
||||
except (IOError, URLError):
|
||||
log.error('Failed to open %s.' % url)
|
||||
return results
|
||||
|
||||
if data:
|
||||
try:
|
||||
try:
|
||||
if cached:
|
||||
xml = self.cache[cacheId]['xml']
|
||||
else:
|
||||
xml = self.getItems(data)
|
||||
self.cache[cacheId]['xml'] = xml
|
||||
except:
|
||||
log.debug('No valid xml or to many requests.' % self.name)
|
||||
return results
|
||||
|
||||
results = []
|
||||
for nzb in xml:
|
||||
|
||||
for item in nzb:
|
||||
if item.attrib.get('name') == 'size':
|
||||
size = item.attrib.get('value')
|
||||
elif item.attrib.get('name') == 'usenetdate':
|
||||
date = item.attrib.get('value')
|
||||
|
||||
new = self.feedItem()
|
||||
new.id = self.gettextelement(nzb, "guid").split('/')[-1:].pop()
|
||||
new.type = 'nzb'
|
||||
new.name = self.gettextelement(nzb, "title")
|
||||
new.date = int(time.mktime(parse(date).timetuple()))
|
||||
new.size = int(size) / 1024 / 1024
|
||||
new.url = self.downloadLink(new.id)
|
||||
new.detailUrl = self.detailLink(new.id)
|
||||
new.content = self.gettextelement(nzb, "description")
|
||||
new.score = self.calcScore(new, movie)
|
||||
|
||||
if new.date > time.time() - (int(self.config.get('NZB', 'retention')) * 24 * 60 * 60) and self.isCorrectMovie(new, movie, type, imdbResults = True, singleCategory = singleCat):
|
||||
results.append(new)
|
||||
log.info('Found: %s' % new.name)
|
||||
|
||||
return results
|
||||
except SyntaxError:
|
||||
log.error('Failed to parse XML response from Newznab')
|
||||
return False
|
||||
|
||||
return results
|
||||
|
||||
def isEnabled(self):
|
||||
return NZBProvider.isEnabled(self) and self.conf('enabled') and self.conf('host') and self.conf('apikey')
|
||||
|
||||
def getApiExt(self):
|
||||
return '&apikey=%s' % self.conf('apikey')
|
||||
|
||||
def downloadLink(self, id):
|
||||
return self.getUrl(self.downloadUrl) % (id, self.getApiExt())
|
||||
|
||||
def detailLink(self, id):
|
||||
return self.getUrl(self.detailUrl) % id
|
||||
|
||||
@@ -20,7 +20,7 @@ config = [{
|
||||
},
|
||||
{
|
||||
'name': 'api_key',
|
||||
'default': '9b939aee0aaafc12a65bf448e4af9543',
|
||||
'default': '',
|
||||
'label': 'Api Key',
|
||||
},
|
||||
],
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
from couchpotato.core.event import addEvent
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.providers.base import NZBProvider
|
||||
from dateutil.parser import parse
|
||||
from urllib import urlencode
|
||||
from urllib2 import URLError
|
||||
import time
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
@@ -20,12 +25,10 @@ class NZBMatrix(NZBProvider):
|
||||
]
|
||||
cat_backup_id = 2
|
||||
|
||||
def __init__(self, config):
|
||||
log.info('Using NZBMatrix provider')
|
||||
def __init__(self):
|
||||
addEvent('provider.nzb.search', self.search)
|
||||
|
||||
self.config = config
|
||||
|
||||
def find(self, movie, quality, type, retry = False):
|
||||
def search(self, movie, quality):
|
||||
|
||||
self.cleanCache();
|
||||
|
||||
@@ -113,4 +116,4 @@ class NZBMatrix(NZBProvider):
|
||||
return '&username=%s&apikey=%s' % (self.conf('username'), self.conf('apikey'))
|
||||
|
||||
def isEnabled(self):
|
||||
return self.conf('enabled') and self.conf('username') and self.conf('apikey')
|
||||
return NZBProvider.isEnabled(self) and self.conf('enabled') and self.conf('username') and self.conf('apikey')
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
from app.config.cplog import CPLog
|
||||
from app.lib.provider.yarr.base import nzbBase
|
||||
from dateutil.parser import parse
|
||||
from urllib import urlencode
|
||||
from urllib2 import URLError
|
||||
import time
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
class nzbMatrix(nzbBase):
|
||||
"""Api for NZBMatrix"""
|
||||
|
||||
name = 'NZBMatrix'
|
||||
downloadUrl = 'https://api.nzbmatrix.com/v1.1/download.php?id=%s%s'
|
||||
detailUrl = 'https://nzbmatrix.com/nzb-details.php?id=%s&hit=1'
|
||||
searchUrl = 'http://rss.nzbmatrix.com/rss.php'
|
||||
|
||||
catIds = {
|
||||
42: ['720p', '1080p'],
|
||||
2: ['cam', 'ts', 'dvdrip', 'tc', 'r5', 'scr'],
|
||||
54: ['brrip'],
|
||||
1: ['dvdr']
|
||||
}
|
||||
catBackupId = 2
|
||||
|
||||
timeBetween = 10 # Seconds
|
||||
|
||||
def __init__(self, config):
|
||||
log.info('Using NZBMatrix provider')
|
||||
|
||||
self.config = config
|
||||
|
||||
def conf(self, option):
|
||||
return self.config.get('NZBMatrix', option)
|
||||
|
||||
def enabled(self):
|
||||
return self.conf('enabled') and self.config.get('NZB', 'enabled') and self.conf('username') and self.conf('apikey')
|
||||
|
||||
def find(self, movie, quality, type, retry = False):
|
||||
|
||||
self.cleanCache();
|
||||
|
||||
results = []
|
||||
if not self.enabled() or not self.isAvailable(self.searchUrl):
|
||||
return results
|
||||
|
||||
catId = self.getCatId(type)
|
||||
arguments = urlencode({
|
||||
'term': movie.imdb,
|
||||
'subcat': catId,
|
||||
'username': self.conf('username'),
|
||||
'apikey': self.conf('apikey'),
|
||||
'searchin': 'weblink',
|
||||
'english': 1 if self.conf('english') else 0,
|
||||
})
|
||||
url = "%s?%s" % (self.searchUrl, arguments)
|
||||
cacheId = str(movie.imdb) + '-' + str(catId)
|
||||
singleCat = (len(self.catIds.get(catId)) == 1 and catId != self.catBackupId)
|
||||
|
||||
try:
|
||||
cached = False
|
||||
if(self.cache.get(cacheId)):
|
||||
data = True
|
||||
cached = True
|
||||
log.info('Getting RSS from cache: %s.' % cacheId)
|
||||
else:
|
||||
log.info('Searching: %s' % url)
|
||||
data = self.urlopen(url)
|
||||
self.cache[cacheId] = {
|
||||
'time': time.time()
|
||||
}
|
||||
|
||||
except (IOError, URLError):
|
||||
log.error('Failed to open %s.' % url)
|
||||
return results
|
||||
|
||||
if data:
|
||||
try:
|
||||
try:
|
||||
if cached:
|
||||
xml = self.cache[cacheId]['xml']
|
||||
else:
|
||||
xml = self.getItems(data)
|
||||
self.cache[cacheId]['xml'] = xml
|
||||
except:
|
||||
log.debug('No valid xml or to many requests.. You never know with %s.' % self.name)
|
||||
return results
|
||||
|
||||
for nzb in xml:
|
||||
|
||||
title = self.gettextelement(nzb, "title")
|
||||
if 'error' in title.lower(): continue
|
||||
|
||||
id = int(self.gettextelement(nzb, "link").split('&')[0].partition('id=')[2])
|
||||
size = self.gettextelement(nzb, "description").split('<br /><b>')[2].split('> ')[1]
|
||||
date = str(self.gettextelement(nzb, "description").split('<br /><b>')[3].partition('Added:</b> ')[2])
|
||||
|
||||
new = self.feedItem()
|
||||
new.id = id
|
||||
new.type = 'nzb'
|
||||
new.name = title
|
||||
new.date = int(time.mktime(parse(date).timetuple()))
|
||||
new.size = self.parseSize(size)
|
||||
new.url = self.downloadLink(id)
|
||||
new.detailUrl = self.detailLink(id)
|
||||
new.content = self.gettextelement(nzb, "description")
|
||||
new.score = self.calcScore(new, movie)
|
||||
new.checkNZB = True
|
||||
|
||||
if new.date > time.time() - (int(self.config.get('NZB', 'retention')) * 24 * 60 * 60):
|
||||
if self.isCorrectMovie(new, movie, type, imdbResults = True, singleCategory = singleCat):
|
||||
results.append(new)
|
||||
log.info('Found: %s' % new.name)
|
||||
else:
|
||||
log.info('Found outside retention: %s' % new.name)
|
||||
|
||||
return results
|
||||
except SyntaxError:
|
||||
log.error('Failed to parse XML response from NZBMatrix.com')
|
||||
|
||||
return results
|
||||
|
||||
def getApiExt(self):
|
||||
return '&username=%s&apikey=%s' % (self.conf('username'), self.conf('apikey'))
|
||||
@@ -1,8 +1,122 @@
|
||||
from couchpotato.core.event import addEvent
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.providers.base import NZBProvider
|
||||
from dateutil.parser import parse
|
||||
from urllib import urlencode
|
||||
from urllib2 import URLError
|
||||
import time
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
|
||||
class Nzbs(NZBProvider):
|
||||
pass
|
||||
|
||||
urls = {
|
||||
'download': 'http://nzbs.org/index.php?action=getnzb&nzbid=%s%s',
|
||||
'nfo': 'http://nzbs.org/index.php?action=view&nzbid=%s&nfo=1',
|
||||
'detail': 'http://nzbs.org/index.php?action=view&nzbid=%s',
|
||||
'api': 'http://nzbs.org/rss.php',
|
||||
}
|
||||
|
||||
cat_ids = [
|
||||
([4], ['720p', '1080p']),
|
||||
([2], ['cam', 'ts', 'dvdrip', 'tc', 'brrip', 'r5', 'scr']),
|
||||
([9], ['dvdr']),
|
||||
]
|
||||
cat_backup_id = 't2'
|
||||
|
||||
time_between_searches = 3 # Seconds
|
||||
|
||||
def __init__(self):
|
||||
addEvent('provider.nzb.search', self.search)
|
||||
|
||||
def search(self, movie, quality):
|
||||
|
||||
self.cleanCache();
|
||||
|
||||
results = []
|
||||
if not self.enabled() or not self.isAvailable(self.apiUrl + '?test' + self.getApiExt()):
|
||||
return results
|
||||
|
||||
catId = self.getCatId(type)
|
||||
arguments = urlencode({
|
||||
'action':'search',
|
||||
'q': self.toSearchString(movie.name),
|
||||
'catid': catId,
|
||||
'i': self.conf('id'),
|
||||
'h': self.conf('key'),
|
||||
'age': self.config.get('NZB', 'retention')
|
||||
})
|
||||
url = "%s?%s" % (self.apiUrl, arguments)
|
||||
cacheId = str(movie.imdb) + '-' + str(catId)
|
||||
singleCat = (len(self.catIds.get(catId)) == 1 and catId != self.catBackupId)
|
||||
|
||||
try:
|
||||
cached = False
|
||||
if(self.cache.get(cacheId)):
|
||||
data = True
|
||||
cached = True
|
||||
log.info('Getting RSS from cache: %s.' % cacheId)
|
||||
else:
|
||||
log.info('Searching: %s' % url)
|
||||
data = self.urlopen(url)
|
||||
self.cache[cacheId] = {
|
||||
'time': time.time()
|
||||
}
|
||||
except (IOError, URLError):
|
||||
log.error('Failed to open %s.' % url)
|
||||
return results
|
||||
|
||||
if data:
|
||||
log.debug('Parsing NZBs.org RSS.')
|
||||
try:
|
||||
try:
|
||||
if cached:
|
||||
xml = self.cache[cacheId]['xml']
|
||||
else:
|
||||
xml = self.getItems(data)
|
||||
self.cache[cacheId]['xml'] = xml
|
||||
except:
|
||||
retry = False
|
||||
if retry == False:
|
||||
log.error('No valid xml, to many requests? Try again in 15sec.')
|
||||
time.sleep(15)
|
||||
return self.find(movie, quality, type, retry = True)
|
||||
else:
|
||||
log.error('Failed again.. disable %s for 15min.' % self.name)
|
||||
self.available = False
|
||||
return results
|
||||
|
||||
for nzb in xml:
|
||||
|
||||
id = int(self.gettextelement(nzb, "link").partition('nzbid=')[2])
|
||||
|
||||
size = self.gettextelement(nzb, "description").split('</a><br />')[1].split('">')[1]
|
||||
|
||||
new = self.feedItem()
|
||||
new.id = id
|
||||
new.type = 'nzb'
|
||||
new.name = self.gettextelement(nzb, "title")
|
||||
new.date = int(time.mktime(parse(self.gettextelement(nzb, "pubDate")).timetuple()))
|
||||
new.size = self.parseSize(size)
|
||||
new.url = self.downloadLink(id)
|
||||
new.detailUrl = self.detailLink(id)
|
||||
new.content = self.gettextelement(nzb, "description")
|
||||
new.score = self.calcScore(new, movie)
|
||||
|
||||
if self.isCorrectMovie(new, movie, type, singleCategory = singleCat):
|
||||
results.append(new)
|
||||
log.info('Found: %s' % new.name)
|
||||
|
||||
return results
|
||||
except SyntaxError:
|
||||
log.error('Failed to parse XML response from NZBs.org')
|
||||
return False
|
||||
|
||||
return results
|
||||
|
||||
def isEnabled(self):
|
||||
return NZBProvider.isEnabled(self) and self.conf('enabled') and self.conf('id') and self.conf('key')
|
||||
|
||||
def getApiExt(self):
|
||||
return '&i=%s&h=%s' % (self.conf('id'), self.conf('key'))
|
||||
|
||||
@@ -5,4 +5,144 @@ log = CPLog(__name__)
|
||||
|
||||
|
||||
class ThePirateBay(TorrentProvider):
|
||||
pass
|
||||
|
||||
urls = {
|
||||
'download': 'http://torrents.thepiratebay.org/%s/%s.torrent',
|
||||
'nfo': 'http://thepiratebay.org/torrent/%s',
|
||||
'detail': 'http://thepiratebay.org/torrent/%s',
|
||||
'search': 'http://thepiratebay.org/search/%s/0/7/%d',
|
||||
}
|
||||
|
||||
cat_ids = [
|
||||
([207], ['720p', '1080p']),
|
||||
([200], ['cam', 'ts', 'dvdrip', 'tc', 'r5', 'scr', 'brrip']),
|
||||
([202], ['dvdr'])
|
||||
]
|
||||
cat_backup_id = 200
|
||||
|
||||
ignore_string = {
|
||||
'720p': ' -brrip -bdrip',
|
||||
'1080p': ' -brrip -bdrip'
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def find(self, movie, quality, type):
|
||||
|
||||
results = []
|
||||
if not self.enabled() or not self.isAvailable(self.apiUrl):
|
||||
return results
|
||||
|
||||
url = self.apiUrl % (quote_plus(self.toSearchString(movie.name + ' ' + quality) + self.makeIgnoreString(type)), self.getCatId(type))
|
||||
|
||||
log.info('Searching: %s' % url)
|
||||
|
||||
try:
|
||||
data = urllib2.urlopen(url, timeout = self.timeout).read()
|
||||
except (IOError, URLError):
|
||||
log.error('Failed to open %s.' % url)
|
||||
return results
|
||||
|
||||
try:
|
||||
tables = SoupStrainer('table')
|
||||
html = BeautifulSoup(data, parseOnlyThese = tables)
|
||||
resultTable = html.find('table', attrs = {'id':'searchResult'})
|
||||
for result in resultTable.findAll('tr'):
|
||||
details = result.find('a', attrs = {'class':'detLink'})
|
||||
if details:
|
||||
href = re.search('/(?P<id>\d+)/', details['href'])
|
||||
id = href.group('id')
|
||||
name = self.toSaveString(details.contents[0])
|
||||
desc = result.find('font', attrs = {'class':'detDesc'}).contents[0].split(',')
|
||||
date = ''
|
||||
size = 0
|
||||
for item in desc:
|
||||
# Weird date stuff
|
||||
if 'uploaded' in item.lower():
|
||||
date = item.replace('Uploaded', '')
|
||||
date = date.replace('Today', '')
|
||||
|
||||
# Do something with yesterday
|
||||
yesterdayMinus = 0
|
||||
if 'Y-day' in date:
|
||||
date = date.replace('Y-day', '')
|
||||
yesterdayMinus = 86400
|
||||
|
||||
datestring = date.replace(' ', ' ').strip()
|
||||
date = int(time.mktime(parse(datestring).timetuple())) - yesterdayMinus
|
||||
# size
|
||||
elif 'size' in item.lower():
|
||||
size = item.replace('Size', '')
|
||||
|
||||
seedleech = []
|
||||
for td in result.findAll('td'):
|
||||
try:
|
||||
seedleech.append(int(td.contents[0]))
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
seeders = 0
|
||||
leechers = 0
|
||||
if len(seedleech) == 2 and seedleech[0] > 0 and seedleech[1] > 0:
|
||||
seeders = seedleech[0]
|
||||
leechers = seedleech[1]
|
||||
|
||||
# to item
|
||||
new = self.feedItem()
|
||||
new.id = id
|
||||
new.type = 'torrent'
|
||||
new.name = name
|
||||
new.date = date
|
||||
new.size = self.parseSize(size)
|
||||
new.seeders = seeders
|
||||
new.leechers = leechers
|
||||
new.url = self.downloadLink(id, name)
|
||||
new.score = self.calcScore(new, movie) + self.uploader(result) + (seeders / 10)
|
||||
|
||||
if seeders > 0 and (new.date + (int(self.conf('wait')) * 60 * 60) < time.time()) and Qualities.types.get(type).get('minSize') <= new.size:
|
||||
new.detailUrl = self.detailLink(id)
|
||||
new.content = self.getInfo(new.detailUrl)
|
||||
if self.isCorrectMovie(new, movie, type):
|
||||
results.append(new)
|
||||
log.info('Found: %s' % new.name)
|
||||
|
||||
return results
|
||||
|
||||
except AttributeError:
|
||||
log.debug('No search results found.')
|
||||
|
||||
return []
|
||||
|
||||
def makeIgnoreString(self, type):
|
||||
ignore = self.ignoreString.get(type)
|
||||
return ignore if ignore else ''
|
||||
|
||||
def uploader(self, html):
|
||||
score = 0
|
||||
if html.find('img', attr = {'alt':'VIP'}):
|
||||
score += 3
|
||||
if html.find('img', attr = {'alt':'Trusted'}):
|
||||
score += 1
|
||||
return score
|
||||
|
||||
|
||||
def getInfo(self, url):
|
||||
log.debug('Getting info: %s' % url)
|
||||
try:
|
||||
data = urllib2.urlopen(url, timeout = self.timeout).read()
|
||||
pass
|
||||
except IOError:
|
||||
log.error('Failed to open %s.' % url)
|
||||
return ''
|
||||
|
||||
div = SoupStrainer('div')
|
||||
html = BeautifulSoup(data, parseOnlyThese = div)
|
||||
html = html.find('div', attrs = {'class':'nfo'})
|
||||
return str(html).decode("utf-8", "replace")
|
||||
|
||||
def downloadLink(self, id, name):
|
||||
return self.downloadUrl % (id, quote_plus(name))
|
||||
|
||||
def isEnabled(self):
|
||||
return self.conf('enabled') and TorrentProvider.isEnabled(self)
|
||||
|
||||
@@ -45,7 +45,7 @@ class Settings():
|
||||
for option, value in options.iteritems():
|
||||
self.setDefault(section_name, option, value)
|
||||
|
||||
self.log.debug('Defaults for "%s": %s' % (section_name, options))
|
||||
#self.log.debug('Defaults for "%s": %s' % (section_name, options))
|
||||
|
||||
if save:
|
||||
self.save(self)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// MooTools: the javascript framework.
|
||||
// Load this file's selection again by visiting: http://mootools.net/more/2b832e45b9bf2f9e5fdbdafc9b16febf
|
||||
// Or build this file again with packager using: packager build More/Element.Forms More/Element.Delegation More/Element.Shortcuts More/Fx.Slide More/Sortables More/Request.JSONP More/Spinner
|
||||
// Load this file's selection again by visiting: http://mootools.net/more/1e3edb90c5e02d9b9013b54e6ab001ea
|
||||
// Or build this file again with packager using: packager build More/Element.Forms More/Element.Delegation More/Element.Shortcuts More/Fx.Slide More/Sortables More/Request.JSONP More/Request.Periodical More/Spinner
|
||||
/*
|
||||
---
|
||||
|
||||
@@ -1754,6 +1754,59 @@ Request.JSONP.counter = 0;
|
||||
Request.JSONP.request_map = {};
|
||||
|
||||
|
||||
/*
|
||||
---
|
||||
|
||||
script: Request.Periodical.js
|
||||
|
||||
name: Request.Periodical
|
||||
|
||||
description: Requests the same URL to pull data from a server but increases the intervals if no data is returned to reduce the load
|
||||
|
||||
license: MIT-style license
|
||||
|
||||
authors:
|
||||
- Christoph Pojer
|
||||
|
||||
requires:
|
||||
- Core/Request
|
||||
- /MooTools.More
|
||||
|
||||
provides: [Request.Periodical]
|
||||
|
||||
...
|
||||
*/
|
||||
|
||||
Request.implement({
|
||||
|
||||
options: {
|
||||
initialDelay: 5000,
|
||||
delay: 5000,
|
||||
limit: 60000
|
||||
},
|
||||
|
||||
startTimer: function(data){
|
||||
var fn = function(){
|
||||
if (!this.running) this.send({data: data});
|
||||
};
|
||||
this.lastDelay = this.options.initialDelay;
|
||||
this.timer = fn.delay(this.lastDelay, this);
|
||||
this.completeCheck = function(response){
|
||||
clearTimeout(this.timer);
|
||||
this.lastDelay = (response) ? this.options.delay : (this.lastDelay + this.options.delay).min(this.options.limit);
|
||||
this.timer = fn.delay(this.lastDelay, this);
|
||||
};
|
||||
return this.addEvent('complete', this.completeCheck);
|
||||
},
|
||||
|
||||
stopTimer: function(){
|
||||
clearTimeout(this.timer);
|
||||
return this.removeEvent('complete', this.completeCheck);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
/*
|
||||
---
|
||||
|
||||
|
||||
@@ -9,7 +9,8 @@ Page.Settings = new Class({
|
||||
'general': {},
|
||||
'providers': {},
|
||||
'downloaders': {},
|
||||
'notifications': {}
|
||||
'notifications': {},
|
||||
'renamer': {}
|
||||
},
|
||||
|
||||
open: function(action, params){
|
||||
@@ -114,15 +115,16 @@ Page.Settings = new Class({
|
||||
section.groups.sortBy('order').each(function(group){
|
||||
|
||||
// Create the group
|
||||
var group_el = self.createGroup(group).inject(self.tabs[group.tab].content);
|
||||
|
||||
self.tabs[group.tab].groups[group.name] = group_el
|
||||
if(!self.tabs[group.tab].groups[group.name]){
|
||||
var group_el = self.createGroup(group).inject(self.tabs[group.tab].content);
|
||||
self.tabs[group.tab].groups[group.name] = group_el
|
||||
}
|
||||
|
||||
// Add options to group
|
||||
group.options.sortBy('order').each(function(option){
|
||||
var class_name = (option.type || 'string').capitalize();
|
||||
var input = new Option[class_name](self, section_name, option.name, option);
|
||||
input.inject(group_el);
|
||||
input.inject(self.tabs[group.tab].groups[group.name]);
|
||||
input.fireEvent('injected')
|
||||
});
|
||||
|
||||
@@ -142,7 +144,7 @@ Page.Settings = new Class({
|
||||
if(self.tabs[tab_name] && self.tabs[tab_name].tab)
|
||||
return self.tabs[tab_name].tab
|
||||
|
||||
var label = (tab.label || tab.name).capitalize()
|
||||
var label = (tab.label || tab.name || tab_name).capitalize()
|
||||
var tab_el = new Element('li').adopt(
|
||||
new Element('a', {
|
||||
'href': '/'+self.name+'/'+tab_name+'/',
|
||||
|
||||
@@ -27,9 +27,9 @@
|
||||
<script type="text/javascript" src="{{ url_for('.static', filename='scripts/page/manage.js') }}"></script>
|
||||
|
||||
{% for url in fireEvent('clientscript.get_scripts', as_html = True, single = True) %}
|
||||
<script type="text/javascript" src="{{ url_for('api.index') }}{{ url }}"></script>{% endfor %}
|
||||
<script type="text/javascript" src="{{ url_for('web.index') }}{{ url }}"></script>{% endfor %}
|
||||
{% for url in fireEvent('clientscript.get_styles', as_html = True, single = True) %}
|
||||
<link rel="stylesheet" href="{{ url_for('api.index') }}{{ url }}" type="text/css">{% endfor %}
|
||||
<link rel="stylesheet" href="{{ url_for('web.index') }}{{ url }}" type="text/css">{% endfor %}
|
||||
|
||||
<link href="{{ url_for('.static', filename='images/favicon.ico') }}" rel="icon" type="image/x-icon" />
|
||||
<link rel="apple-touch-icon" href="{{ url_for('.static', filename='images/homescreen.png') }}" />
|
||||
|
||||
Reference in New Issue
Block a user