Switched event system (again)
This commit is contained in:
1 parent
fd5a41435b
commit
f9523444f4
24 files changed
+580
-142
No files matched your search
@@ -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.core.settings.model import Resource
|
||||||
from couchpotato.environment import Env
|
|
||||||
from flask import Module
|
from flask import Module
|
||||||
from flask.helpers import jsonify
|
from flask.helpers import jsonify
|
||||||
import flask
|
|
||||||
|
|
||||||
api = Module(__name__)
|
api = Module(__name__)
|
||||||
|
|
||||||
|
def addApiView(route, func):
|
||||||
|
api.add_url_rule(route + '/', route, func)
|
||||||
|
|
||||||
|
|
||||||
@api.route('')
|
@api.route('')
|
||||||
def index():
|
def index():
|
||||||
return jsonify({'test': 'bla'})
|
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/')
|
@api.route('movie/')
|
||||||
def movie():
|
def movie():
|
||||||
|
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'success': True,
|
'success': True,
|
||||||
'movies': [
|
'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 import get_engine, web
|
||||||
from couchpotato.api import api
|
from couchpotato.api import api
|
||||||
from couchpotato.core.settings.model import *
|
from couchpotato.core.settings.model import *
|
||||||
from couchpotato.environment import Env
|
|
||||||
from libs.daemon import createDaemon
|
from libs.daemon import createDaemon
|
||||||
from logging import handlers
|
from logging import handlers
|
||||||
import logging
|
import logging
|
||||||
@@ -44,10 +43,12 @@ def cmd_couchpotato(base_path, args):
|
|||||||
|
|
||||||
|
|
||||||
# Register environment settings
|
# Register environment settings
|
||||||
|
from couchpotato.environment import Env
|
||||||
Env.get('settings').setFile(os.path.join(options.data_dir, 'settings.conf'))
|
Env.get('settings').setFile(os.path.join(options.data_dir, 'settings.conf'))
|
||||||
Env.set('app_dir', base_path)
|
Env.set('app_dir', base_path)
|
||||||
Env.set('data_dir', options.data_dir)
|
Env.set('data_dir', options.data_dir)
|
||||||
Env.set('db_path', 'sqlite:///' + os.path.join(options.data_dir, 'couchpotato.db'))
|
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('quiet', options.quiet)
|
||||||
Env.set('daemonize', options.daemonize)
|
Env.set('daemonize', options.daemonize)
|
||||||
Env.set('args', args)
|
Env.set('args', args)
|
||||||
@@ -84,11 +85,11 @@ def cmd_couchpotato(base_path, args):
|
|||||||
log.debug('Started with options %s' % options)
|
log.debug('Started with options %s' % options)
|
||||||
|
|
||||||
|
|
||||||
# Load configs
|
# Load configs & plugins
|
||||||
from couchpotato.core.settings.loader import settings_loader
|
loader = Env.get('loader')
|
||||||
settings_loader.load(root = base_path)
|
loader.preload(root = base_path)
|
||||||
settings_loader.addConfig('couchpotato', 'core')
|
loader.addModule('core', 'couchpotato.core', 'core')
|
||||||
settings_loader.run()
|
loader.run()
|
||||||
|
|
||||||
|
|
||||||
# Load migrations
|
# Load migrations
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
|
def start():
|
||||||
|
pass
|
||||||
|
|
||||||
config = [{
|
config = [{
|
||||||
'name': 'global',
|
'name': 'global',
|
||||||
'tab': 'general',
|
'tab': 'general',
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ def requires_auth(f):
|
|||||||
|
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def decorated(*args, **kwargs):
|
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)):
|
if Env.setting('username') and (not auth or not check_auth(auth.username, auth.password)):
|
||||||
return authenticate()
|
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 os
|
||||||
import string
|
import string
|
||||||
|
|
||||||
@@ -9,6 +12,8 @@ class FileBrowser():
|
|||||||
def __init__(self, path = '/'):
|
def __init__(self, path = '/'):
|
||||||
self.path = path
|
self.path = path
|
||||||
|
|
||||||
|
addApiView('directory.list', self.view)
|
||||||
|
|
||||||
def getDirectories(self):
|
def getDirectories(self):
|
||||||
|
|
||||||
# Return driveletters or root if path is empty
|
# Return driveletters or root if path is empty
|
||||||
@@ -36,3 +41,16 @@ class FileBrowser():
|
|||||||
driveletters.append(drive + ":")
|
driveletters.append(drive + ":")
|
||||||
|
|
||||||
return driveletters
|
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 = [{
|
config = [{
|
||||||
'name': 'Renamer',
|
'name': 'Renamer',
|
||||||
'tab': 'renaming',
|
'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 = [{
|
config = [{
|
||||||
'name': 'TheMovieDB',
|
'name': 'TheMovieDB',
|
||||||
'tab': 'providers',
|
'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 __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 ConfigParser
|
||||||
import os.path
|
import os.path
|
||||||
import time
|
import time
|
||||||
@@ -7,10 +9,13 @@ import time
|
|||||||
|
|
||||||
class Settings():
|
class Settings():
|
||||||
|
|
||||||
on_save = Signal()
|
|
||||||
on_register = Signal()
|
|
||||||
|
|
||||||
bool = {'true':True, 'false':False}
|
bool = {'true':True, 'false':False}
|
||||||
|
options = {}
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
|
||||||
|
addApiView('settings', self.view)
|
||||||
|
addApiView('settings.save', self.saveView)
|
||||||
|
|
||||||
def setFile(self, file):
|
def setFile(self, file):
|
||||||
self.file = file
|
self.file = file
|
||||||
@@ -21,7 +26,7 @@ class Settings():
|
|||||||
from couchpotato.core.logger import CPLog
|
from couchpotato.core.logger import CPLog
|
||||||
self.log = CPLog(__name__)
|
self.log = CPLog(__name__)
|
||||||
|
|
||||||
self.connectSignals()
|
self.connectEvents()
|
||||||
|
|
||||||
def parser(self):
|
def parser(self):
|
||||||
return self.p
|
return self.p
|
||||||
@@ -29,18 +34,17 @@ class Settings():
|
|||||||
def sections(self):
|
def sections(self):
|
||||||
return self.p.sections()
|
return self.p.sections()
|
||||||
|
|
||||||
def connectSignals(self):
|
def connectEvents(self):
|
||||||
signal('settings.register').connect(self.registerDefaults)
|
addEvent('settings.options', self.addOptions)
|
||||||
signal('settings.save').connect(self.save)
|
addEvent('settings.register', self.registerDefaults)
|
||||||
|
addEvent('settings.save', self.save)
|
||||||
|
|
||||||
def registerDefaults(self, section_name, options = {}, save = True):
|
def registerDefaults(self, section_name, options = {}, save = True):
|
||||||
|
|
||||||
self.addSection(section_name)
|
self.addSection(section_name)
|
||||||
for option, value in options.iteritems():
|
for option, value in options.iteritems():
|
||||||
self.setDefault(section_name, option, value)
|
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))
|
||||||
self.on_register.send(self)
|
|
||||||
|
|
||||||
if save:
|
if save:
|
||||||
self.save(self)
|
self.save(self)
|
||||||
@@ -73,12 +77,11 @@ class Settings():
|
|||||||
values[section][option_name] = option_value
|
values[section][option_name] = option_value
|
||||||
return values
|
return values
|
||||||
|
|
||||||
def save(self, caller = ''):
|
def save(self):
|
||||||
with open(self.file, 'wb') as configfile:
|
with open(self.file, 'wb') as configfile:
|
||||||
self.p.write(configfile)
|
self.p.write(configfile)
|
||||||
|
|
||||||
self.log.debug('Saved settings')
|
self.log.debug('Saved settings')
|
||||||
self.on_save.send(self)
|
|
||||||
|
|
||||||
def addSection(self, section):
|
def addSection(self, section):
|
||||||
if not self.p.has_section(section):
|
if not self.p.has_section(section):
|
||||||
@@ -94,3 +97,32 @@ class Settings():
|
|||||||
return True
|
return True
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return False
|
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
|
from couchpotato.core.settings import Settings
|
||||||
|
import flask
|
||||||
|
|
||||||
class Env:
|
class Env:
|
||||||
|
|
||||||
''' Environment variables '''
|
''' Environment variables '''
|
||||||
_debug = False
|
_debug = False
|
||||||
_settings = Settings()
|
_settings = Settings()
|
||||||
|
_loader = Loader()
|
||||||
_options = None
|
_options = None
|
||||||
_args = None
|
_args = None
|
||||||
_quiet = False
|
_quiet = False
|
||||||
@@ -13,6 +16,7 @@ class Env:
|
|||||||
''' Data paths and directories '''
|
''' Data paths and directories '''
|
||||||
_app_dir = ""
|
_app_dir = ""
|
||||||
_data_dir = ""
|
_data_dir = ""
|
||||||
|
_cache_dir = ""
|
||||||
_db_path = ""
|
_db_path = ""
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -38,3 +42,11 @@ class Env:
|
|||||||
s = Env.get('settings')
|
s = Env.get('settings')
|
||||||
s.set(section, attr, value)
|
s.set(section, attr, value)
|
||||||
return s
|
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;
|
var self = this;
|
||||||
self.setOptions(options);
|
self.setOptions(options);
|
||||||
|
|
||||||
self.parent = parent;
|
self.page = parent;
|
||||||
|
|
||||||
self.create();
|
self.create();
|
||||||
},
|
},
|
||||||
@@ -17,6 +17,14 @@ var BlockBase = new Class({
|
|||||||
this.el = new Element('div.block');
|
this.el = new Element('div.block');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
api: function(){
|
||||||
|
return this.getParent().getApi()
|
||||||
|
},
|
||||||
|
|
||||||
|
getParent: function(){
|
||||||
|
return this.page
|
||||||
|
},
|
||||||
|
|
||||||
toElement: function(){
|
toElement: function(){
|
||||||
return this.el
|
return this.el
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,37 @@ Block.Search = new Class({
|
|||||||
create: function(){
|
create: function(){
|
||||||
var self = this;
|
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){
|
request: function(type, options){
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
new Request.JSON(Object.merge({
|
return new Request.JSON(Object.merge({
|
||||||
'method': 'get',
|
'method': 'get',
|
||||||
'url': self.createUrl(type),
|
'url': self.createUrl(type),
|
||||||
}, options)).send()
|
}, options)).send()
|
||||||
|
|||||||
Reference in new issue
Block a user