diff --git a/couchpotato/api/__init__.py b/couchpotato/api/__init__.py index 2b0639a..901bbac 100644 --- a/couchpotato/api/__init__.py +++ b/couchpotato/api/__init__.py @@ -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, - }) diff --git a/couchpotato/cli.py b/couchpotato/cli.py index 704ec6a..270dc19 100644 --- a/couchpotato/cli.py +++ b/couchpotato/cli.py @@ -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 diff --git a/couchpotato/core/__init__.py b/couchpotato/core/__init__.py index f0aaee7..954a651 100644 --- a/couchpotato/core/__init__.py +++ b/couchpotato/core/__init__.py @@ -1,5 +1,8 @@ from uuid import uuid4 +def start(): + pass + config = [{ 'name': 'global', 'tab': 'general', diff --git a/couchpotato/core/auth.py b/couchpotato/core/auth.py index de0979a..325a575 100644 --- a/couchpotato/core/auth.py +++ b/couchpotato/core/auth.py @@ -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() diff --git a/couchpotato/core/event.py b/couchpotato/core/event.py new file mode 100644 index 0000000..793a915 --- /dev/null +++ b/couchpotato/core/event.py @@ -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] diff --git a/couchpotato/core/helpers.py b/couchpotato/core/helpers.py new file mode 100644 index 0000000..112ecf7 --- /dev/null +++ b/couchpotato/core/helpers.py @@ -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 diff --git a/couchpotato/core/loader.py b/couchpotato/core/loader.py new file mode 100644 index 0000000..1806366 --- /dev/null +++ b/couchpotato/core/loader.py @@ -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 diff --git a/couchpotato/core/plugins/base.py b/couchpotato/core/plugins/base.py new file mode 100644 index 0000000..8691b2b --- /dev/null +++ b/couchpotato/core/plugins/base.py @@ -0,0 +1,4 @@ +class Plugin(): + + def __init__(self): + pass diff --git a/couchpotato/core/plugins/file_browser/__init__.py b/couchpotato/core/plugins/file_browser/__init__.py new file mode 100644 index 0000000..a15b213 --- /dev/null +++ b/couchpotato/core/plugins/file_browser/__init__.py @@ -0,0 +1,6 @@ +from couchpotato.core.plugins.file_browser.main import FileBrowser + +def start(): + return FileBrowser() + +config = [] diff --git a/couchpotato/api/file_browser.py b/couchpotato/core/plugins/file_browser/main.py similarity index 67% rename from couchpotato/api/file_browser.py rename to couchpotato/core/plugins/file_browser/main.py index f274e09..619dc8b 100644 --- a/couchpotato/api/file_browser.py +++ b/couchpotato/core/plugins/file_browser/main.py @@ -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, + }) diff --git a/couchpotato/core/plugins/movie_add/__init__.py b/couchpotato/core/plugins/movie_add/__init__.py new file mode 100644 index 0000000..a77db65 --- /dev/null +++ b/couchpotato/core/plugins/movie_add/__init__.py @@ -0,0 +1,6 @@ +from couchpotato.core.plugins.movie_add.main import MovieAdd + +def start(): + return MovieAdd() + +config = [] diff --git a/couchpotato/core/plugins/movie_add/main.py b/couchpotato/core/plugins/movie_add/main.py new file mode 100644 index 0000000..e82a845 --- /dev/null +++ b/couchpotato/core/plugins/movie_add/main.py @@ -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 diff --git a/couchpotato/core/plugins/renamer/__init__.py b/couchpotato/core/plugins/renamer/__init__.py index 9f014bd..a5d6e26 100644 --- a/couchpotato/core/plugins/renamer/__init__.py +++ b/couchpotato/core/plugins/renamer/__init__.py @@ -1,3 +1,6 @@ +def start(): + pass + config = [{ 'name': 'Renamer', 'tab': 'renaming', diff --git a/couchpotato/core/providers/base.py b/couchpotato/core/providers/base.py new file mode 100644 index 0000000..1e20b98 --- /dev/null +++ b/couchpotato/core/providers/base.py @@ -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 [] diff --git a/couchpotato/core/providers/imdb/__init__.py b/couchpotato/core/providers/imdb/__init__.py new file mode 100644 index 0000000..3442847 --- /dev/null +++ b/couchpotato/core/providers/imdb/__init__.py @@ -0,0 +1,4 @@ +def start(): + pass + +config = [] diff --git a/couchpotato/core/providers/imdb/main.py b/couchpotato/core/providers/imdb/main.py new file mode 100644 index 0000000..2f7c20b --- /dev/null +++ b/couchpotato/core/providers/imdb/main.py @@ -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 diff --git a/couchpotato/core/providers/tmdb/__init__.py b/couchpotato/core/providers/tmdb/__init__.py index 2e1bc40..96647aa 100644 --- a/couchpotato/core/providers/tmdb/__init__.py +++ b/couchpotato/core/providers/tmdb/__init__.py @@ -1,3 +1,8 @@ +from couchpotato.core.providers.tmdb.main import TMDB + +def start(): + return TMDB() + config = [{ 'name': 'TheMovieDB', 'tab': 'providers', diff --git a/couchpotato/core/providers/tmdb/main.py b/couchpotato/core/providers/tmdb/main.py new file mode 100644 index 0000000..484e9ce --- /dev/null +++ b/couchpotato/core/providers/tmdb/main.py @@ -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 diff --git a/couchpotato/core/settings/__init__.py b/couchpotato/core/settings/__init__.py index 6a98b37..0d088c2 100644 --- a/couchpotato/core/settings/__init__.py +++ b/couchpotato/core/settings/__init__.py @@ -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, + }) diff --git a/couchpotato/core/settings/loader.py b/couchpotato/core/settings/loader.py deleted file mode 100644 index 70687e3..0000000 --- a/couchpotato/core/settings/loader.py +++ /dev/null @@ -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() diff --git a/couchpotato/environment.py b/couchpotato/environment.py index 1d1138f..fd742f6 100644 --- a/couchpotato/environment.py +++ b/couchpotato/environment.py @@ -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) diff --git a/couchpotato/static/scripts/block.js b/couchpotato/static/scripts/block.js index 8fea9a5..851db63 100644 --- a/couchpotato/static/scripts/block.js +++ b/couchpotato/static/scripts/block.js @@ -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 } diff --git a/couchpotato/static/scripts/block/search.js b/couchpotato/static/scripts/block/search.js index 23cfe21..a6d2d70 100644 --- a/couchpotato/static/scripts/block/search.js +++ b/couchpotato/static/scripts/block/search.js @@ -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(){ + } }); \ No newline at end of file diff --git a/couchpotato/static/scripts/couchpotato.js b/couchpotato/static/scripts/couchpotato.js index 7434a61..380dfcc 100644 --- a/couchpotato/static/scripts/couchpotato.js +++ b/couchpotato/static/scripts/couchpotato.js @@ -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()