Scheduler
NZBmatrix, newznab Scores
This commit is contained in:
1 parent
9524306739
commit
7837adec1d
38 files changed
+856
-219
No files matched your search
@@ -1 +1,75 @@
|
||||
from uuid import uuid4
|
||||
|
||||
def start():
|
||||
pass
|
||||
|
||||
config = [{
|
||||
'name': 'core',
|
||||
'groups': [
|
||||
{
|
||||
'tab': 'general',
|
||||
'name': 'basics',
|
||||
'description': 'Needs restart before changes take effect.',
|
||||
'options': [
|
||||
{
|
||||
'name': 'username',
|
||||
'default': '',
|
||||
},
|
||||
{
|
||||
'name': 'password',
|
||||
'default': '',
|
||||
'type': 'password',
|
||||
},
|
||||
{
|
||||
'name': 'host',
|
||||
'advanced': True,
|
||||
'default': '0.0.0.0',
|
||||
'label': 'IP',
|
||||
'description': 'Host that I should listen to. "0.0.0.0" listens to all ips.',
|
||||
},
|
||||
{
|
||||
'name': 'port',
|
||||
'default': 5000,
|
||||
'type': 'int',
|
||||
'description': 'The port I should listen to.',
|
||||
},
|
||||
{
|
||||
'name': 'launch_browser',
|
||||
'default': 1,
|
||||
'type': 'bool',
|
||||
'label': 'Launch Browser',
|
||||
'description': 'Launch the browser when I start.',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
'tab': 'general',
|
||||
'name': 'advanced',
|
||||
'description': "For those who know what the're doing",
|
||||
'advanced': True,
|
||||
'options': [
|
||||
{
|
||||
'name': 'api_key',
|
||||
'default': uuid4().hex,
|
||||
'readonly': 1,
|
||||
'label': 'Api Key',
|
||||
'description': "This is top-secret! Don't share this!",
|
||||
},
|
||||
{
|
||||
'name': 'debug',
|
||||
'default': 0,
|
||||
'type': 'bool',
|
||||
'label': 'Debug',
|
||||
'description': 'Enable debugging.',
|
||||
},
|
||||
{
|
||||
'name': 'url_base',
|
||||
'default': '',
|
||||
'label': 'Url Base',
|
||||
'description': 'When using mod_proxy use this to append the url with this.',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}]
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
from .main import ClientScript
|
||||
|
||||
def start():
|
||||
return ClientScript()
|
||||
|
||||
config = []
|
||||
@@ -1,56 +0,0 @@
|
||||
from couchpotato.core.event import addEvent
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
|
||||
class ClientScript(Plugin):
|
||||
|
||||
urls = {
|
||||
'style': {},
|
||||
'script': {},
|
||||
}
|
||||
|
||||
html = {
|
||||
'style': '<link rel="stylesheet" href="%s" type="text/css">',
|
||||
'script': '<script type="text/javascript" src="%s"></script>',
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
addEvent('register_style', self.registerStyle)
|
||||
addEvent('register_script', self.registerScript)
|
||||
|
||||
addEvent('clientscript.get_styles', self.getStyles)
|
||||
addEvent('clientscript.get_scripts', self.getScripts)
|
||||
|
||||
def getStyles(self, *args, **kwargs):
|
||||
return self.get('style', *args, **kwargs)
|
||||
|
||||
def getScripts(self, *args, **kwargs):
|
||||
return self.get('script', *args, **kwargs)
|
||||
|
||||
def get(self, type, as_html = False, location = 'head'):
|
||||
|
||||
data = '' if as_html else []
|
||||
|
||||
try:
|
||||
return self.urls[type][location]
|
||||
except Exception, e:
|
||||
log.error(e)
|
||||
|
||||
return data
|
||||
|
||||
def registerStyle(self, path, position = 'head'):
|
||||
self.register(path, 'style', position)
|
||||
|
||||
def registerScript(self, path, position = 'head'):
|
||||
self.register(path, 'script', position)
|
||||
|
||||
def register(self, file, type, location):
|
||||
|
||||
if not self.urls[type].get(location):
|
||||
self.urls[type][location] = []
|
||||
|
||||
filePath = file
|
||||
self.urls[type][location].append(filePath)
|
||||
@@ -37,4 +37,4 @@ class Plugin():
|
||||
return not self.isEnabled()
|
||||
|
||||
def isEnabled(self):
|
||||
return self.conf('enabled', True)
|
||||
return self.conf('enabled')
|
||||
File renamed without changes.
File renamed without changes.
@@ -27,6 +27,7 @@ class QualityPlugin(Plugin):
|
||||
|
||||
def __init__(self):
|
||||
addEvent('quality.all', self.all)
|
||||
addEvent('quality.single', self.single)
|
||||
addEvent('app.load', self.fill)
|
||||
|
||||
path = self.registerStatic(__file__)
|
||||
@@ -46,6 +47,14 @@ class QualityPlugin(Plugin):
|
||||
|
||||
return temp
|
||||
|
||||
def single(self, identifier = ''):
|
||||
|
||||
db = get_session()
|
||||
|
||||
quality = db.query(Quality).filter_by(identifier = identifier).first()
|
||||
|
||||
return dict(self.getQuality(quality.identifier), **quality.to_dict())
|
||||
|
||||
def getQuality(self, identifier):
|
||||
|
||||
for q in self.qualities:
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
from couchpotato.core.event import addEvent, fireEvent
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
|
||||
@@ -8,3 +9,11 @@ class Renamer(Plugin):
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
addEvent('renamer.scan', self.scan)
|
||||
addEvent('app.load', self.scan)
|
||||
|
||||
fireEvent('schedule.interval', 'renamer.scan', self.scan, minutes = self.conf('run_every'))
|
||||
|
||||
def scan(self):
|
||||
print 'scan'
|
||||
@@ -0,0 +1,6 @@
|
||||
from .main import Score
|
||||
|
||||
def start():
|
||||
return Score()
|
||||
|
||||
config = []
|
||||
@@ -0,0 +1,22 @@
|
||||
from couchpotato.core.event import addEvent
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
from couchpotato.core.plugins.score.scores import nameScore, nameRatioScore
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
|
||||
class Score(Plugin):
|
||||
|
||||
def __init__(self):
|
||||
addEvent('score.calculate', self.calculate)
|
||||
|
||||
def calculate(self, nzb, movie):
|
||||
''' Calculate the score of a NZB, used for sorting later '''
|
||||
|
||||
score = nameScore(nzb['name'], movie['library']['year'])
|
||||
|
||||
for movie_title in movie['library']['titles']:
|
||||
score += nameRatioScore(nzb['name'], movie_title['title'])
|
||||
|
||||
return score
|
||||
@@ -0,0 +1,51 @@
|
||||
from couchpotato.core.helpers.encoding import simplifyString
|
||||
from couchpotato.environment import Env
|
||||
import re
|
||||
|
||||
name_scores = [
|
||||
'proper:2', 'repack:2',
|
||||
'unrated:1',
|
||||
'x264:1',
|
||||
'DTS:4', 'AC3:2',
|
||||
'720p:10', '1080p:10', 'bluray:10', 'dvd:1', 'dvdrip:1', 'brrip:1', 'bdrip:1',
|
||||
'metis:1', 'diamond:1', 'wiki:1', 'CBGB:1',
|
||||
'german:-10', 'french:-10', 'spanish:-10', 'swesub:-20', 'danish:-10'
|
||||
]
|
||||
|
||||
def nameScore(name, year):
|
||||
''' Calculate score for words in the NZB name '''
|
||||
|
||||
score = 0
|
||||
name = name.lower()
|
||||
|
||||
#give points for the cool stuff
|
||||
for value in name_scores:
|
||||
v = value.split(':')
|
||||
add = int(v.pop())
|
||||
if v.pop() in name:
|
||||
score = score + add
|
||||
|
||||
#points if the year is correct
|
||||
if str(year) in name:
|
||||
score = score + 1
|
||||
|
||||
# Contains preferred word
|
||||
nzb_words = re.split('\W+', simplifyString(name))
|
||||
preferred_words = Env.setting('preferred_words', section = 'searcher').split(',')
|
||||
for word in preferred_words:
|
||||
if word.strip() and word.strip().lower() in nzb_words:
|
||||
score = score + 100
|
||||
|
||||
return score
|
||||
|
||||
def nameRatioScore(nzb_name, movie_name):
|
||||
|
||||
nzb_words = re.split('\W+', simplifyString(nzb_name))
|
||||
movie_words = re.split('\W+', simplifyString(movie_name))
|
||||
|
||||
# Replace .,-_ with space
|
||||
left_over = len(nzb_words) - len(movie_words)
|
||||
if 2 <= left_over <= 6:
|
||||
return 4
|
||||
else:
|
||||
return 0
|
||||
@@ -0,0 +1,78 @@
|
||||
from .main import Searcher
|
||||
import random
|
||||
|
||||
def start():
|
||||
return Searcher()
|
||||
|
||||
config = [{
|
||||
'name': 'searcher',
|
||||
'groups': [
|
||||
{
|
||||
'tab': 'searcher',
|
||||
'name': 'searcher',
|
||||
'label': 'Search',
|
||||
'description': 'Options for the searchers',
|
||||
'options': [
|
||||
{
|
||||
'name': 'required_words',
|
||||
'label': 'Required words',
|
||||
'default': '',
|
||||
},
|
||||
{
|
||||
'name': 'ignored_words',
|
||||
'label': 'Ignored words',
|
||||
'default': '',
|
||||
},
|
||||
],
|
||||
}, {
|
||||
'tab': 'searcher',
|
||||
'name': 'searcher',
|
||||
'label': 'Cronjob',
|
||||
'advanced': True,
|
||||
'description': 'Cron settings for the searcher see: <a href="http://packages.python.org/APScheduler/cronschedule.html">APScheduler</a> for details.',
|
||||
'options': [
|
||||
{
|
||||
'name': 'cron_day',
|
||||
'label': 'Day',
|
||||
'advanced': True,
|
||||
'default': '*',
|
||||
'type': 'string',
|
||||
'description': '<strong>*</strong>: Every day, <strong>*/2</strong>: Every 2 days, <strong>1</strong>: Every first of the month.',
|
||||
},
|
||||
{
|
||||
'name': 'cron_hour',
|
||||
'label': 'Hour',
|
||||
'advanced': True,
|
||||
'default': random.randint(0, 23),
|
||||
'type': 'string',
|
||||
'description': '<strong>*</strong>: Every hour, <strong>*/8</strong>: Every 8 hours, <strong>3</strong>: At 3, midnight.',
|
||||
},
|
||||
{
|
||||
'name': 'cron_minute',
|
||||
'label': 'Minute',
|
||||
'advanced': True,
|
||||
'default': random.randint(0, 59),
|
||||
'type': 'string',
|
||||
'description': "Just keep it random, so the providers don't get DDOSed by every CP user on a 'full' hour."
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}, {
|
||||
'name': 'nzb',
|
||||
'groups': [
|
||||
{
|
||||
'tab': 'searcher',
|
||||
'name': 'nzb',
|
||||
'label': 'NZB',
|
||||
'options': [
|
||||
{
|
||||
'name': 'retention',
|
||||
'default': 350,
|
||||
'type': 'int',
|
||||
'unit': 'days'
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}]
|
||||
@@ -0,0 +1,156 @@
|
||||
from couchpotato import get_session
|
||||
from couchpotato.core.event import addEvent, fireEvent
|
||||
from couchpotato.core.helpers.encoding import simplifyString
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
from couchpotato.core.settings.model import Movie
|
||||
from couchpotato.environment import Env
|
||||
import re
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
|
||||
class Searcher(Plugin):
|
||||
|
||||
def __init__(self):
|
||||
addEvent('searcher.all', self.all)
|
||||
addEvent('searcher.single', self.single)
|
||||
addEvent('searcher.correct_movie', self.correctMovie)
|
||||
|
||||
# Schedule cronjob
|
||||
fireEvent('schedule.cron', 'searcher.all', self.all, day = self.conf('cron_day'), hour = self.conf('cron_hour'), minute = self.conf('cron_minute'))
|
||||
|
||||
def all(self):
|
||||
|
||||
db = get_session()
|
||||
|
||||
movies = db.query(Movie).filter(
|
||||
Movie.status.has(identifier = 'active')
|
||||
).all()
|
||||
|
||||
for movie in movies:
|
||||
self.single(movie.to_dict(deep = {
|
||||
'profile': {'types': {'quality': {}}},
|
||||
'releases': {'status': {}, 'quality': {}},
|
||||
'library': {'titles': {}, 'files':{}},
|
||||
'files': {}
|
||||
}))
|
||||
|
||||
|
||||
def single(self, movie):
|
||||
|
||||
for type in movie['profile']['types']:
|
||||
results = fireEvent('provider.yarr.search', movie, type['quality'], merge = True)
|
||||
sorted_results = sorted(results, key = lambda k: k['score'], reverse = True)
|
||||
for nzb in sorted_results:
|
||||
print nzb['name']
|
||||
|
||||
|
||||
def correctMovie(self, nzb = {}, movie = {}, quality = {}, **kwargs):
|
||||
|
||||
imdb_results = kwargs.get('imdb_results', False)
|
||||
single_category = kwargs.get('single_category', False)
|
||||
retention = Env.setting('retention', section = 'nzb')
|
||||
|
||||
if retention < nzb.get('age', 0):
|
||||
log.info('Wrong: Outside retention, age = %s, needs = %s: %s' % (nzb['age'], retention, nzb['name']))
|
||||
return False
|
||||
|
||||
nzb_words = re.split('\W+', simplifyString(nzb['name']))
|
||||
required_words = self.conf('required_words').split(',')
|
||||
|
||||
if self.conf('required_words') and not list(set(nzb_words) & set(required_words)):
|
||||
log.info("NZB doesn't contain any of the required words.")
|
||||
return False
|
||||
|
||||
ignored_words = self.conf('ignored_words').split(',')
|
||||
blacklisted = list(set(nzb_words) & set(ignored_words))
|
||||
if self.conf('ignored_words') and blacklisted:
|
||||
log.info("NZB '%s' contains the following blacklisted words: %s" % (nzb['name'], ", ".join(blacklisted)))
|
||||
return False
|
||||
|
||||
#qualities = fireEvent('quality.all', single = True)
|
||||
preferred_quality = fireEvent('quality.single', identifier = quality['identifier'], single = True)
|
||||
|
||||
# Contains lower quality string
|
||||
if self.containsOtherQuality(nzb['name'], preferred_quality, single_category):
|
||||
log.info('Wrong: %s, looking for %s' % (nzb['name'], quality['label']))
|
||||
return False
|
||||
|
||||
"""
|
||||
# File to small
|
||||
minSize = q.minimumSize(qualityType)
|
||||
if minSize > item.size:
|
||||
log.info('"%s" is too small to be %s. %sMB instead of the minimal of %sMB.' % (item.name, type['label'], item.size, minSize))
|
||||
return False
|
||||
|
||||
# File to large
|
||||
maxSize = q.maximumSize(qualityType)
|
||||
if maxSize < item.size:
|
||||
log.info('"%s" is too large to be %s. %sMB instead of the maximum of %sMB.' % (item.name, type['label'], item.size, maxSize))
|
||||
return False
|
||||
|
||||
"""
|
||||
|
||||
if imdb_results:
|
||||
return True
|
||||
|
||||
# Check if nzb contains imdb link
|
||||
if self.checkIMDB([nzb['description']], movie['library']['identifier']):
|
||||
return True
|
||||
|
||||
for movie_title in movie['library']['titles']:
|
||||
movie_words = re.split('\W+', simplifyString(movie_title['title']))
|
||||
|
||||
if self.correctName(nzb['name'], movie_title['title']):
|
||||
# if no IMDB link, at least check year range 1
|
||||
if len(movie_words) > 2 and self.correctYear([nzb['name']], movie['library']['year'], 1):
|
||||
return True
|
||||
|
||||
# if no IMDB link, at least check year
|
||||
if len(movie_words) == 2 and self.correctYear([nzb['name']], movie['library']['year'], 0):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def containsOtherQuality(self, name, preferred_quality = {}, single_category = False):
|
||||
|
||||
nzb_words = re.split('\W+', simplifyString(name))
|
||||
|
||||
qualities = fireEvent('quality.all', single = True)
|
||||
|
||||
found = {}
|
||||
for quality in qualities:
|
||||
# Main in words
|
||||
if quality['identifier'] in nzb_words:
|
||||
found[quality['identifier']] = True
|
||||
|
||||
# Alt in words
|
||||
if list(set(nzb_words) & set(quality['alternative'])):
|
||||
found[quality['identifier']] = True
|
||||
|
||||
# Allow other qualities
|
||||
for allowed in preferred_quality.get('allow'):
|
||||
if found.get(allowed):
|
||||
del found[allowed]
|
||||
|
||||
if (len(found) == 0 and single_category):
|
||||
return False
|
||||
|
||||
return not (found.get(preferred_quality['identifier']) and len(found) == 1)
|
||||
|
||||
def checkIMDB(self, haystack, imdbId):
|
||||
|
||||
for string in haystack:
|
||||
if 'imdb.com/title/' + imdbId in string:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def correctYear(self, haystack, year, range):
|
||||
|
||||
for string in haystack:
|
||||
if str(year) in string or str(int(year) + range) in string or str(int(year) - range) in string: # 1 year of is fine too
|
||||
return True
|
||||
|
||||
return False
|
||||
@@ -13,7 +13,7 @@ config = [{
|
||||
{
|
||||
'name': 'show_wizard',
|
||||
'label': 'Run the wizard',
|
||||
'default': True,
|
||||
'default': 1,
|
||||
'type': 'bool',
|
||||
},
|
||||
],
|
||||
|
||||
@@ -193,7 +193,7 @@ var Spotlight = new Class({
|
||||
'position': 'absolute',
|
||||
'background-color': 'rgba('+self.options.fillColor.join(',')+', '+self.options.fillOpacity+')',
|
||||
'display': 'block',
|
||||
'z-index': 2,
|
||||
'z-index': 999,
|
||||
'top': self.top[nr],
|
||||
'left': self.left[nr],
|
||||
'height': self.height[nr],
|
||||
@@ -276,7 +276,7 @@ var Spotlight = new Class({
|
||||
var self = this;
|
||||
|
||||
var soften = self.options.soften;
|
||||
var edge = new Element('div', {
|
||||
var edge = new Element('div.edge', {
|
||||
'styles': Object.merge({
|
||||
'position': 'absolute',
|
||||
'width': soften,
|
||||
|
||||
@@ -6,13 +6,22 @@ var WizardBase = new Class({
|
||||
var self = this;
|
||||
|
||||
self.steps = steps;
|
||||
self.start();
|
||||
self.spotlight = new Spotlight([], {
|
||||
'fillColor': [0,0,0],
|
||||
'soften': 0
|
||||
});
|
||||
window.addEvent('resize', self.spotlight.create.bind(self.spotlight))
|
||||
|
||||
},
|
||||
|
||||
start: function(){
|
||||
var self = this;
|
||||
|
||||
var els = $(document.body).getElements("[name='core[username]'], [name='core[password]'], [name='core[launch_browser]']").getParent('.ctrlHolder')
|
||||
|
||||
p(els, self.spotlight.setElements(els))
|
||||
|
||||
self.spotlight.create();
|
||||
|
||||
},
|
||||
|
||||
|
||||
Reference in new issue
Block a user