Switched event system (again)
This commit is contained in:
@@ -1,42 +1,20 @@
|
||||
from couchpotato.api.file_browser import FileBrowser
|
||||
from couchpotato.core.settings.loader import settings_loader
|
||||
from couchpotato.core.settings.model import Resource
|
||||
from couchpotato.environment import Env
|
||||
from flask import Module
|
||||
from flask.helpers import jsonify
|
||||
import flask
|
||||
|
||||
api = Module(__name__)
|
||||
|
||||
def addApiView(route, func):
|
||||
api.add_url_rule(route + '/', route, func)
|
||||
|
||||
|
||||
@api.route('')
|
||||
def index():
|
||||
return jsonify({'test': 'bla'})
|
||||
|
||||
|
||||
@api.route('settings/')
|
||||
def settings_view():
|
||||
return jsonify({
|
||||
'sections': settings_loader.sections,
|
||||
'values': Env.get('settings').getValues()
|
||||
})
|
||||
|
||||
@api.route('setting.save/')
|
||||
def setting_save_view():
|
||||
a = flask.request.args
|
||||
|
||||
section = a.get('section')
|
||||
option = a.get('name')
|
||||
value = a.get('value')
|
||||
|
||||
Env.setting(option, section, value).save()
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
});
|
||||
|
||||
@api.route('movie/')
|
||||
def movie():
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'movies': [
|
||||
@@ -50,18 +28,3 @@ def movie():
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
@api.route('directory.list/')
|
||||
def director_list():
|
||||
a = flask.request.args
|
||||
|
||||
try:
|
||||
fb = FileBrowser(a.get('path', '/'))
|
||||
dirs = fb.getDirectories()
|
||||
except:
|
||||
dirs = []
|
||||
|
||||
return jsonify({
|
||||
'empty': len(dirs) == 0,
|
||||
'dirs': dirs,
|
||||
})
|
||||
|
||||
+7
-6
@@ -2,7 +2,6 @@ from argparse import ArgumentParser
|
||||
from couchpotato import get_engine, web
|
||||
from couchpotato.api import api
|
||||
from couchpotato.core.settings.model import *
|
||||
from couchpotato.environment import Env
|
||||
from libs.daemon import createDaemon
|
||||
from logging import handlers
|
||||
import logging
|
||||
@@ -44,10 +43,12 @@ def cmd_couchpotato(base_path, args):
|
||||
|
||||
|
||||
# Register environment settings
|
||||
from couchpotato.environment import Env
|
||||
Env.get('settings').setFile(os.path.join(options.data_dir, 'settings.conf'))
|
||||
Env.set('app_dir', base_path)
|
||||
Env.set('data_dir', options.data_dir)
|
||||
Env.set('db_path', 'sqlite:///' + os.path.join(options.data_dir, 'couchpotato.db'))
|
||||
Env.set('cache_dir', os.path.join(options.data_dir, 'cache'))
|
||||
Env.set('quiet', options.quiet)
|
||||
Env.set('daemonize', options.daemonize)
|
||||
Env.set('args', args)
|
||||
@@ -84,11 +85,11 @@ def cmd_couchpotato(base_path, args):
|
||||
log.debug('Started with options %s' % options)
|
||||
|
||||
|
||||
# Load configs
|
||||
from couchpotato.core.settings.loader import settings_loader
|
||||
settings_loader.load(root = base_path)
|
||||
settings_loader.addConfig('couchpotato', 'core')
|
||||
settings_loader.run()
|
||||
# Load configs & plugins
|
||||
loader = Env.get('loader')
|
||||
loader.preload(root = base_path)
|
||||
loader.addModule('core', 'couchpotato.core', 'core')
|
||||
loader.run()
|
||||
|
||||
|
||||
# Load migrations
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
from uuid import uuid4
|
||||
|
||||
def start():
|
||||
pass
|
||||
|
||||
config = [{
|
||||
'name': 'global',
|
||||
'tab': 'general',
|
||||
|
||||
@@ -15,7 +15,7 @@ def requires_auth(f):
|
||||
|
||||
@wraps(f)
|
||||
def decorated(*args, **kwargs):
|
||||
auth = request.authorization
|
||||
auth = getattr(request, 'authorization')
|
||||
if Env.setting('username') and (not auth or not check_auth(auth.username, auth.password)):
|
||||
return authenticate()
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
from couchpotato.core.logger import CPLog
|
||||
from axl.axel import Event
|
||||
|
||||
log = CPLog(__name__)
|
||||
events = {}
|
||||
|
||||
def addEvent(name, handler):
|
||||
|
||||
if events.get(name):
|
||||
e = events[name]
|
||||
else:
|
||||
e = events[name] = Event(threads = 20, exc_info = True, traceback = True)
|
||||
|
||||
e += handler
|
||||
|
||||
def removeEvent(name, handler):
|
||||
e = events[name]
|
||||
e -= handler
|
||||
|
||||
def fireEvent(name, *args, **kwargs):
|
||||
try:
|
||||
e = events[name]
|
||||
e.asynchronous = False
|
||||
return e(*args, **kwargs)
|
||||
except Exception, e:
|
||||
log.debug(e)
|
||||
|
||||
def fireEventAsync(name, *args, **kwargs):
|
||||
try:
|
||||
e = events[name]
|
||||
e.asynchronous = True
|
||||
return e(*args, **kwargs)
|
||||
except Exception, e:
|
||||
log.debug(e)
|
||||
|
||||
def getEvent(name):
|
||||
return events[name]
|
||||
@@ -0,0 +1,37 @@
|
||||
def latinToAscii(unicrap):
|
||||
xlate = {0xc0:'A', 0xc1:'A', 0xc2:'A', 0xc3:'A', 0xc4:'A', 0xc5:'A',
|
||||
0xc6:'Ae', 0xc7:'C',
|
||||
0xc8:'E', 0xc9:'E', 0xca:'E', 0xcb:'E', 0x86:'e',
|
||||
0xcc:'I', 0xcd:'I', 0xce:'I', 0xcf:'I',
|
||||
0xd0:'Th', 0xd1:'N',
|
||||
0xd2:'O', 0xd3:'O', 0xd4:'O', 0xd5:'O', 0xd6:'O', 0xd8:'O',
|
||||
0xd9:'U', 0xda:'U', 0xdb:'U', 0xdc:'U',
|
||||
0xdd:'Y', 0xde:'th', 0xdf:'ss',
|
||||
0xe0:'a', 0xe1:'a', 0xe2:'a', 0xe3:'a', 0xe4:'a', 0xe5:'a',
|
||||
0xe6:'ae', 0xe7:'c',
|
||||
0xe8:'e', 0xe9:'e', 0xea:'e', 0xeb:'e',
|
||||
0xec:'i', 0xed:'i', 0xee:'i', 0xef:'i',
|
||||
0xf0:'th', 0xf1:'n',
|
||||
0xf2:'o', 0xf3:'o', 0xf4:'o', 0xf5:'o', 0xf6:'o', 0xf8:'o',
|
||||
0xf9:'u', 0xfa:'u', 0xfb:'u', 0xfc:'u',
|
||||
0xfd:'y', 0xfe:'th', 0xff:'y',
|
||||
0xa1:'!', 0xa2:'{cent}', 0xa3:'{pound}', 0xa4:'{currency}',
|
||||
0xa5:'{yen}', 0xa6:'|', 0xa7:'{section}', 0xa8:'{umlaut}',
|
||||
0xa9:'{C}', 0xaa:'{^a}', 0xab:'<<', 0xac:'{not}',
|
||||
0xad:'-', 0xae:'{R}', 0xaf:'_', 0xb0:'{degrees}',
|
||||
0xb1:'{+/-}', 0xb2:'{^2}', 0xb3:'{^3}', 0xb4:"'",
|
||||
0xb5:'{micro}', 0xb6:'{paragraph}', 0xb7:'*', 0xb8:'{cedilla}',
|
||||
0xb9:'{^1}', 0xba:'{^o}', 0xbb:'>>',
|
||||
0xbc:'{1/4}', 0xbd:'{1/2}', 0xbe:'{3/4}', 0xbf:'?',
|
||||
0xd7:'*', 0xf7:'/'
|
||||
}
|
||||
|
||||
r = ''
|
||||
for i in unicrap:
|
||||
if xlate.has_key(ord(i)):
|
||||
r += xlate[ord(i)]
|
||||
elif ord(i) >= 0x80:
|
||||
pass
|
||||
else:
|
||||
r += str(i)
|
||||
return r
|
||||
@@ -0,0 +1,86 @@
|
||||
from couchpotato.core.event import fireEvent, fireEventAsync
|
||||
from couchpotato.core.logger import CPLog
|
||||
import glob
|
||||
import os
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
class Loader:
|
||||
|
||||
plugins = {}
|
||||
providers = {}
|
||||
|
||||
modules = {}
|
||||
|
||||
def preload(self, root = ''):
|
||||
|
||||
self.paths = {
|
||||
'plugin' : ('couchpotato.core.plugins', os.path.join(root, 'couchpotato', 'core', 'plugins')),
|
||||
'provider' : ('couchpotato.core.providers', os.path.join(root, 'couchpotato', 'core', 'providers')),
|
||||
}
|
||||
|
||||
for type, tuple in self.paths.iteritems():
|
||||
self.addFromDir(type, tuple[0], tuple[1])
|
||||
|
||||
def run(self):
|
||||
did_save = 0
|
||||
|
||||
for module_name, plugin in sorted(self.modules.iteritems()):
|
||||
|
||||
# Load module
|
||||
m = getattr(self.loadModule(module_name), plugin.get('name'))
|
||||
|
||||
log.info("Loading '%s'" % module_name)
|
||||
|
||||
# Save default settings for plugin/provider
|
||||
did_save += self.loadSettings(m, module_name, save = False)
|
||||
|
||||
self.loadPlugins(m, plugin.get('name'))
|
||||
|
||||
if did_save:
|
||||
fireEvent('settings.save')
|
||||
|
||||
def addFromDir(self, type, 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)
|
||||
|
||||
def loadSettings(self, module, name, save = True):
|
||||
try:
|
||||
for section in module.config:
|
||||
fireEventAsync('settings.options', section['name'], section)
|
||||
options = {}
|
||||
for key, option in section['options'].iteritems():
|
||||
options[key] = option['default']
|
||||
fireEventAsync('settings.register', section_name = section['name'], options = options, save = save)
|
||||
return True
|
||||
except Exception, e:
|
||||
log.debug("Failed loading settings for '%s': %s" % (name, e))
|
||||
return False
|
||||
|
||||
def loadPlugins(self, module, name):
|
||||
try:
|
||||
module.start()
|
||||
return True
|
||||
except Exception, e:
|
||||
log.debug("Failed loading plugin '%s': %s" % (name, e))
|
||||
return False
|
||||
|
||||
def addModule(self, type, module, name):
|
||||
self.modules[module] = {
|
||||
'type': type,
|
||||
'name': name,
|
||||
}
|
||||
|
||||
def loadModule(self, name):
|
||||
try:
|
||||
m = __import__(name)
|
||||
splitted = name.split('.')
|
||||
for sub in splitted[1:-1]:
|
||||
m = getattr(m, sub)
|
||||
return m
|
||||
except:
|
||||
raise
|
||||
@@ -0,0 +1,4 @@
|
||||
class Plugin():
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
@@ -0,0 +1,6 @@
|
||||
from couchpotato.core.plugins.file_browser.main import FileBrowser
|
||||
|
||||
def start():
|
||||
return FileBrowser()
|
||||
|
||||
config = []
|
||||
@@ -1,3 +1,6 @@
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.environment import Env
|
||||
from flask.helpers import jsonify
|
||||
import os
|
||||
import string
|
||||
|
||||
@@ -9,6 +12,8 @@ class FileBrowser():
|
||||
def __init__(self, path = '/'):
|
||||
self.path = path
|
||||
|
||||
addApiView('directory.list', self.view)
|
||||
|
||||
def getDirectories(self):
|
||||
|
||||
# Return driveletters or root if path is empty
|
||||
@@ -36,3 +41,16 @@ class FileBrowser():
|
||||
driveletters.append(drive + ":")
|
||||
|
||||
return driveletters
|
||||
|
||||
def view(self):
|
||||
|
||||
try:
|
||||
fb = FileBrowser(Env.getParam('path', '/'))
|
||||
dirs = fb.getDirectories()
|
||||
except:
|
||||
dirs = []
|
||||
|
||||
return jsonify({
|
||||
'empty': len(dirs) == 0,
|
||||
'dirs': dirs,
|
||||
})
|
||||
@@ -0,0 +1,6 @@
|
||||
from couchpotato.core.plugins.movie_add.main import MovieAdd
|
||||
|
||||
def start():
|
||||
return MovieAdd()
|
||||
|
||||
config = []
|
||||
@@ -0,0 +1,30 @@
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.event import getEvent, fireEvent
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
from couchpotato.environment import Env
|
||||
from flask.helpers import jsonify
|
||||
|
||||
class MovieAdd(Plugin):
|
||||
|
||||
def __init__(self):
|
||||
addApiView('movie.add.search', self.search)
|
||||
|
||||
def search(self):
|
||||
|
||||
a = Env.getParams()
|
||||
|
||||
print fireEvent('provider.movie.search', q = a.get('q'))
|
||||
|
||||
movies = [
|
||||
{'id': 1, 'name': 'test'}
|
||||
]
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'empty': len(movies) == 0,
|
||||
'movies': movies,
|
||||
})
|
||||
|
||||
|
||||
def select(self):
|
||||
pass
|
||||
@@ -1,3 +1,6 @@
|
||||
def start():
|
||||
pass
|
||||
|
||||
config = [{
|
||||
'name': 'Renamer',
|
||||
'tab': 'renaming',
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
from couchpotato.core.helpers import latinToAscii
|
||||
from couchpotato.core.logger import CPLog
|
||||
from string import ascii_letters, digits
|
||||
import re
|
||||
import unicodedata
|
||||
import xml.etree.ElementTree as XMLTree
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
class Provider():
|
||||
|
||||
type = None # movie, nzb, torrent, subtitle, trailer
|
||||
timeout = 10 # Default timeout for url requests
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def toSaveString(self, string):
|
||||
string = latinToAscii(string)
|
||||
string = ''.join((c for c in unicodedata.normalize('NFD', unicode(string)) if unicodedata.category(c) != 'Mn'))
|
||||
safe_chars = ascii_letters + digits + '_ -.,\':!?'
|
||||
r = ''.join([char if char in safe_chars else ' ' for char in string])
|
||||
return re.sub('\s+' , ' ', r)
|
||||
|
||||
def toSearchString(self, string):
|
||||
string = latinToAscii(string)
|
||||
string = ''.join((c for c in unicodedata.normalize('NFD', unicode(string)) if unicodedata.category(c) != 'Mn'))
|
||||
safe_chars = ascii_letters + digits + ' \''
|
||||
r = ''.join([char if char in safe_chars else ' ' for char in string])
|
||||
return re.sub('\s+' , ' ', r).replace('\'s', 's').replace('\'', ' ')
|
||||
|
||||
def gettextelements(self, xml, path):
|
||||
''' Find elements and return tree'''
|
||||
|
||||
textelements = []
|
||||
try:
|
||||
elements = xml.findall(path)
|
||||
except:
|
||||
return
|
||||
for element in elements:
|
||||
textelements.append(element.text)
|
||||
return textelements
|
||||
|
||||
def gettextelement(self, xml, path):
|
||||
''' Find element and return text'''
|
||||
|
||||
try:
|
||||
return xml.find(path).text
|
||||
except:
|
||||
return
|
||||
|
||||
def getItems(self, data, path = 'channel/item'):
|
||||
try:
|
||||
return XMLTree.parse(data).findall(path)
|
||||
except Exception, e:
|
||||
log.error('Error parsing RSS. %s' % e)
|
||||
return []
|
||||
@@ -0,0 +1,4 @@
|
||||
def start():
|
||||
pass
|
||||
|
||||
config = []
|
||||
@@ -0,0 +1,83 @@
|
||||
from couchpotato.core.event import addEvent
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.providers.base import Provider
|
||||
from imdb import IMDb
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
class IMDB(Provider):
|
||||
|
||||
type = 'movie'
|
||||
|
||||
def __init__(self):
|
||||
|
||||
addEvent('provider.movie.search', self.search)
|
||||
|
||||
self.p = IMDb('http')
|
||||
|
||||
|
||||
def search(self):
|
||||
print 'search'
|
||||
|
||||
def conf(self, option):
|
||||
return self.config.get('IMDB', option)
|
||||
|
||||
def find(self, q, limit = 8, alternative = True):
|
||||
''' Find movie by name '''
|
||||
|
||||
log.info('IMDB - Searching for movie: %s' % q)
|
||||
|
||||
r = self.p.search_movie(q)
|
||||
|
||||
return self.toResults(r, limit)
|
||||
|
||||
def toResults(self, r, limit = 8, one = False):
|
||||
results = []
|
||||
|
||||
if one:
|
||||
new = self.feedItem()
|
||||
new.imdb = 'tt' + r.movieID
|
||||
new.name = self.toSaveString(r['title'])
|
||||
try:
|
||||
new.year = r['year']
|
||||
except:
|
||||
new.year = ''
|
||||
|
||||
return new
|
||||
else :
|
||||
nr = 0
|
||||
for movie in r:
|
||||
results.append(self.toResults(movie, one = True))
|
||||
nr += 1
|
||||
if nr == limit:
|
||||
break
|
||||
|
||||
return results
|
||||
|
||||
def findById(self, id):
|
||||
''' Find movie by TheMovieDB ID '''
|
||||
|
||||
return []
|
||||
|
||||
|
||||
def findByImdbId(self, id, details = False):
|
||||
''' Find movie by IMDB ID '''
|
||||
|
||||
log.info('IMDB - Searching for movie: %s' % str(id))
|
||||
|
||||
r = self.p.get_movie(id.replace('tt', ''))
|
||||
|
||||
if not details:
|
||||
return self.toResults(r, one = True)
|
||||
else:
|
||||
self.p.update(r)
|
||||
self.p.update(r, info = 'release dates')
|
||||
self.p.update(r, info = 'taglines')
|
||||
return r
|
||||
|
||||
def get_IMDb_instance(self):
|
||||
return IMDb('http')
|
||||
|
||||
|
||||
def findReleaseDate(self, movie):
|
||||
pass
|
||||
@@ -1,3 +1,8 @@
|
||||
from couchpotato.core.providers.tmdb.main import TMDB
|
||||
|
||||
def start():
|
||||
return TMDB()
|
||||
|
||||
config = [{
|
||||
'name': 'TheMovieDB',
|
||||
'tab': 'providers',
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
from __future__ import with_statement
|
||||
from couchpotato.core.event import addEvent
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.providers.base import Provider
|
||||
from couchpotato.environment import Env
|
||||
from urllib import quote_plus
|
||||
import urllib2
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
class TMDB(Provider):
|
||||
"""Api for theMovieDb"""
|
||||
|
||||
type = 'movie'
|
||||
apiUrl = 'http://api.themoviedb.org/2.1'
|
||||
imageUrl = 'http://hwcdn.themoviedb.org'
|
||||
|
||||
def __init__(self):
|
||||
addEvent('provider.movie.search', self.search)
|
||||
|
||||
def conf(self, attr):
|
||||
return Env.setting(attr, 'TheMovieDB')
|
||||
|
||||
def search(self, q, limit = 8, alternative = True):
|
||||
''' Find movie by name '''
|
||||
|
||||
if self.isDisabled():
|
||||
return False
|
||||
|
||||
log.debug('TheMovieDB - Searching for movie: %s' % q)
|
||||
|
||||
url = "%s/%s/%s/xml/%s/%s" % (self.apiUrl, 'Movie.search', 'en', self.conf('api_key'), quote_plus(self.toSearchString(q)))
|
||||
|
||||
log.info('Searching: %s' % url)
|
||||
|
||||
data = urllib2.urlopen(url)
|
||||
|
||||
return self.parseXML(data, limit, alternative = alternative)
|
||||
|
||||
def parseXML(self, data, limit, alternative = True):
|
||||
if data:
|
||||
log.debug('TheMovieDB - Parsing RSS')
|
||||
try:
|
||||
xml = self.getItems(data, 'movies/movie')
|
||||
|
||||
results = []
|
||||
nr = 0
|
||||
for movie in xml:
|
||||
id = int(self.gettextelement(movie, "id"))
|
||||
|
||||
name = self.gettextelement(movie, "name")
|
||||
imdb = self.gettextelement(movie, "imdb_id")
|
||||
year = str(self.gettextelement(movie, "released"))[:4]
|
||||
|
||||
# 1900 is the same as None
|
||||
if year == '1900':
|
||||
year = 'None'
|
||||
|
||||
results.append({
|
||||
'id': id,
|
||||
'name': self.toSaveString(name),
|
||||
'imdb': imdb,
|
||||
'year': year
|
||||
})
|
||||
|
||||
alternativeName = self.gettextelement(movie, "alternative_name")
|
||||
if alternativeName and alternative:
|
||||
if alternativeName.lower() != name.lower() and alternativeName.lower() != 'none' and alternativeName != None:
|
||||
results.append({
|
||||
'id': id,
|
||||
'name': self.toSaveString(alternativeName),
|
||||
'imdb': imdb,
|
||||
'year': year
|
||||
})
|
||||
|
||||
nr += 1
|
||||
if nr == limit:
|
||||
break
|
||||
|
||||
#log.info('TheMovieDB - Found: %s' % results)
|
||||
return results
|
||||
except SyntaxError:
|
||||
log.error('TheMovieDB - Failed to parse XML response from TheMovieDb')
|
||||
return False
|
||||
|
||||
|
||||
def isDisabled(self):
|
||||
if self.conf('api_key') == '':
|
||||
log.error('TheMovieDB - No API key provided for TheMovieDB')
|
||||
True
|
||||
else:
|
||||
False
|
||||
@@ -1,5 +1,7 @@
|
||||
from __future__ import with_statement
|
||||
from blinker import signal, Signal
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.event import addEvent
|
||||
from flask.helpers import jsonify
|
||||
import ConfigParser
|
||||
import os.path
|
||||
import time
|
||||
@@ -7,10 +9,13 @@ import time
|
||||
|
||||
class Settings():
|
||||
|
||||
on_save = Signal()
|
||||
on_register = Signal()
|
||||
|
||||
bool = {'true':True, 'false':False}
|
||||
options = {}
|
||||
|
||||
def __init__(self):
|
||||
|
||||
addApiView('settings', self.view)
|
||||
addApiView('settings.save', self.saveView)
|
||||
|
||||
def setFile(self, file):
|
||||
self.file = file
|
||||
@@ -21,7 +26,7 @@ class Settings():
|
||||
from couchpotato.core.logger import CPLog
|
||||
self.log = CPLog(__name__)
|
||||
|
||||
self.connectSignals()
|
||||
self.connectEvents()
|
||||
|
||||
def parser(self):
|
||||
return self.p
|
||||
@@ -29,18 +34,17 @@ class Settings():
|
||||
def sections(self):
|
||||
return self.p.sections()
|
||||
|
||||
def connectSignals(self):
|
||||
signal('settings.register').connect(self.registerDefaults)
|
||||
signal('settings.save').connect(self.save)
|
||||
def connectEvents(self):
|
||||
addEvent('settings.options', self.addOptions)
|
||||
addEvent('settings.register', self.registerDefaults)
|
||||
addEvent('settings.save', self.save)
|
||||
|
||||
def registerDefaults(self, section_name, options = {}, save = True):
|
||||
|
||||
self.addSection(section_name)
|
||||
for option, value in options.iteritems():
|
||||
self.setDefault(section_name, option, value)
|
||||
|
||||
self.log.debug('Defaults for "%s": %s' % (section_name, options))
|
||||
self.on_register.send(self)
|
||||
|
||||
if save:
|
||||
self.save(self)
|
||||
@@ -73,12 +77,11 @@ class Settings():
|
||||
values[section][option_name] = option_value
|
||||
return values
|
||||
|
||||
def save(self, caller = ''):
|
||||
def save(self):
|
||||
with open(self.file, 'wb') as configfile:
|
||||
self.p.write(configfile)
|
||||
|
||||
self.log.debug('Saved settings')
|
||||
self.on_save.send(self)
|
||||
|
||||
def addSection(self, section):
|
||||
if not self.p.has_section(section):
|
||||
@@ -94,3 +97,32 @@ class Settings():
|
||||
return True
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
def addOptions(self, section_name, options):
|
||||
self.options[section_name] = options
|
||||
|
||||
def getOptions(self):
|
||||
return self.options
|
||||
|
||||
|
||||
def view(self):
|
||||
|
||||
return jsonify({
|
||||
'options': self.getOptions(),
|
||||
'values': self.getValues()
|
||||
})
|
||||
|
||||
def saveView(self):
|
||||
|
||||
a = Env.getParams()
|
||||
|
||||
section = a.get('section')
|
||||
option = a.get('name')
|
||||
value = a.get('value')
|
||||
|
||||
self.set(option, section, value)
|
||||
self.save()
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
})
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
from blinker import signal
|
||||
from couchpotato.core.logger import CPLog
|
||||
import glob
|
||||
import os
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
class SettingsLoader:
|
||||
|
||||
configs = {}
|
||||
sections = {}
|
||||
|
||||
def __init__(self):
|
||||
self.settings_register = signal('settings.register')
|
||||
self.settings_save = signal('settings.save')
|
||||
|
||||
def load(self, root = ''):
|
||||
|
||||
self.paths = {
|
||||
'plugins' : ('couchpotato.core.plugins', os.path.join(root, 'couchpotato', 'core', 'plugins')),
|
||||
'providers' : ('couchpotato.core.providers', os.path.join(root, 'couchpotato', 'core', 'providers')),
|
||||
}
|
||||
|
||||
for type, tuple in self.paths.iteritems():
|
||||
self.addFromDir(tuple[0], tuple[1])
|
||||
|
||||
def run(self):
|
||||
did_save = 0
|
||||
|
||||
for module, plugin_name in self.configs.iteritems():
|
||||
did_save += self.loadConfig(module, plugin_name, save = False)
|
||||
|
||||
if did_save:
|
||||
self.settings_save.send()
|
||||
|
||||
def addFromDir(self, 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)):
|
||||
self.addConfig(module, name)
|
||||
|
||||
def loadConfig(self, module, name, save = True):
|
||||
module_name = '%s.%s' % (module, name)
|
||||
try:
|
||||
m = getattr(self.loadModule(module_name), name)
|
||||
|
||||
for section in m.config:
|
||||
self.addSection(section['name'], section)
|
||||
options = {}
|
||||
for key, option in section['options'].iteritems():
|
||||
options[key] = option['default']
|
||||
self.settings_register.send(section['name'], options = options, save = save)
|
||||
|
||||
return True
|
||||
except Exception, e:
|
||||
log.error("Failed loading config for %s: %s" % (name, e))
|
||||
return False
|
||||
|
||||
def addConfig(self, module, name):
|
||||
self.configs[module] = name
|
||||
|
||||
def loadModule(self, name):
|
||||
try:
|
||||
m = __import__(name)
|
||||
splitted = name.split('.')
|
||||
for sub in splitted[1:-1]:
|
||||
m = getattr(m, sub)
|
||||
return m
|
||||
except:
|
||||
raise
|
||||
|
||||
def addSection(self, section_name, options):
|
||||
self.sections[section_name] = options
|
||||
|
||||
def getSections(self):
|
||||
return self.sections
|
||||
|
||||
settings_loader = SettingsLoader()
|
||||
@@ -1,10 +1,13 @@
|
||||
from couchpotato.core.loader import Loader
|
||||
from couchpotato.core.settings import Settings
|
||||
import flask
|
||||
|
||||
class Env:
|
||||
|
||||
''' Environment variables '''
|
||||
_debug = False
|
||||
_settings = Settings()
|
||||
_loader = Loader()
|
||||
_options = None
|
||||
_args = None
|
||||
_quiet = False
|
||||
@@ -13,6 +16,7 @@ class Env:
|
||||
''' Data paths and directories '''
|
||||
_app_dir = ""
|
||||
_data_dir = ""
|
||||
_cache_dir = ""
|
||||
_db_path = ""
|
||||
|
||||
@staticmethod
|
||||
@@ -38,3 +42,11 @@ class Env:
|
||||
s = Env.get('settings')
|
||||
s.set(section, attr, value)
|
||||
return s
|
||||
|
||||
@staticmethod
|
||||
def getParams():
|
||||
return getattr(flask.request, 'args')
|
||||
|
||||
@staticmethod
|
||||
def getParam(attr, default = None):
|
||||
return getattr(flask.request, 'args').get(attr, default)
|
||||
|
||||
@@ -8,7 +8,7 @@ var BlockBase = new Class({
|
||||
var self = this;
|
||||
self.setOptions(options);
|
||||
|
||||
self.parent = parent;
|
||||
self.page = parent;
|
||||
|
||||
self.create();
|
||||
},
|
||||
@@ -17,6 +17,14 @@ var BlockBase = new Class({
|
||||
this.el = new Element('div.block');
|
||||
},
|
||||
|
||||
api: function(){
|
||||
return this.getParent().getApi()
|
||||
},
|
||||
|
||||
getParent: function(){
|
||||
return this.page
|
||||
},
|
||||
|
||||
toElement: function(){
|
||||
return this.el
|
||||
}
|
||||
|
||||
@@ -5,7 +5,37 @@ Block.Search = new Class({
|
||||
create: function(){
|
||||
var self = this;
|
||||
|
||||
self.el = new Element('div.search_form');
|
||||
self.el = new Element('div.search_form').adopt(
|
||||
self.input = new Element('input', {
|
||||
'events': {
|
||||
'keyup': self.autocomplete.bind(self)
|
||||
}
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
autocomplete: function(){
|
||||
var self = this;
|
||||
|
||||
if(self.autocomplete_timer) clearTimeout(self.autocomplete_timer)
|
||||
self.autocomplete_timer = self.list.delay(300, self)
|
||||
},
|
||||
|
||||
list: function(){
|
||||
var self = this;
|
||||
|
||||
if(self.api_request) self.api_request.cancel();
|
||||
self.api_request = self.api().request('movie.add.search', {
|
||||
'data': {
|
||||
'q': self.input.get('value')
|
||||
},
|
||||
'onComplete': self.fill.bind(self)
|
||||
})
|
||||
|
||||
},
|
||||
|
||||
fill: function(){
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
@@ -109,7 +109,7 @@ var Api = new Class({
|
||||
request: function(type, options){
|
||||
var self = this;
|
||||
|
||||
new Request.JSON(Object.merge({
|
||||
return new Request.JSON(Object.merge({
|
||||
'method': 'get',
|
||||
'url': self.createUrl(type),
|
||||
}, options)).send()
|
||||
|
||||
Reference in New Issue
Block a user