diff --git a/couchpotato/__init__.py b/couchpotato/__init__.py index 2fb4952..cb8677b 100644 --- a/couchpotato/__init__.py +++ b/couchpotato/__init__.py @@ -1,10 +1,14 @@ from couchpotato.core.auth import requires_auth from couchpotato.core.logger import CPLog +from couchpotato.environment import Env from flask.app import Flask from flask.globals import request from flask.helpers import url_for from flask.module import Module from flask.templating import render_template +from sqlalchemy.engine import create_engine +from sqlalchemy.orm import scoped_session +from sqlalchemy.orm.session import sessionmaker from werkzeug.utils import redirect import os @@ -12,6 +16,12 @@ app = Flask(__name__) log = CPLog(__name__) web = Module(__name__, 'web') +def get_session(engine): + engine = engine if engine else get_engine() + return scoped_session(sessionmaker(autoflush = True, transactional = True, bind = engine)) + +def get_engine(): + return create_engine('sqlite:///' + Env.get('db_path'), echo = Env.get('debug')) @web.route('/') @requires_auth @@ -23,16 +33,3 @@ def page_not_found(error): index_url = url_for('web.index') url = request.path[len(index_url):] return redirect(index_url + '#' + url) - -@web.route('/exit') -@requires_auth -def exit(): - # stopping code - pass - - -@web.route('/restart') -@requires_auth -def restart(): - # restart code - pass diff --git a/couchpotato/api/__init__.py b/couchpotato/api/__init__.py index 4c9c4b8..1f90ec4 100644 --- a/couchpotato/api/__init__.py +++ b/couchpotato/api/__init__.py @@ -1,6 +1,7 @@ from couchpotato.api.file_browser import FileBrowser -from couchpotato.core.settings import settings 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 @@ -16,7 +17,7 @@ def index(): def settings_view(): return jsonify({ 'sections': settings_loader.sections, - 'values': settings.getValues() + 'values': Env.get('settings').getValues() }) @api.route('setting.save/') @@ -27,8 +28,8 @@ def setting_save_view(): option = a.get('name') value = a.get('value') - settings.set(section, option, value) - settings.save() + Env.get('settings').set(section, option, value) + Env.get('settings').save() return jsonify({ 'success': True, @@ -36,6 +37,7 @@ def setting_save_view(): @api.route('movie/') def movie(): + return jsonify({ 'success': True, 'movies': [ diff --git a/couchpotato/cli.py b/couchpotato/cli.py index 89851cd..7e10814 100644 --- a/couchpotato/cli.py +++ b/couchpotato/cli.py @@ -1,6 +1,8 @@ from argparse import ArgumentParser -from couchpotato import web +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 @@ -13,8 +15,8 @@ def cmd_couchpotato(base_path, args): # Options parser = ArgumentParser() - parser.add_argument('-s', '--datadir', default = base_path, - dest = 'data_dir', help = 'Absolute or ~/ path, where settings/logs/database data is saved (default ./)') + parser.add_argument('-s', '--datadir', default = os.path.join(base_path, '_data'), + dest = 'data_dir', help = 'Absolute or ~/ path, where settings/logs/database data is saved (default ./_data)') parser.add_argument('-t', '--test', '--debug', action = 'store_true', dest = 'debug', help = 'Debug mode') parser.add_argument('-q', '--quiet', action = 'store_true', @@ -41,12 +43,15 @@ def cmd_couchpotato(base_path, args): createDaemon() - # Register settings - from couchpotato.core.settings import settings - settings.setFile(os.path.join(options.data_dir, 'settings.conf')) + # Register environment settings + 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', os.path.join(options.data_dir, 'couchpotato.db')) # Determine debug - debug = options.debug or settings.get('debug', default = False) + debug = options.debug or Env.get('settings').get('debug', default = False) + Env.set('debug', debug) # Logger @@ -83,15 +88,21 @@ def cmd_couchpotato(base_path, args): settings_loader.run() + # Configure Database + from elixir import setup_all, create_all + setup_all() + create_all(get_engine()) + + # Create app from couchpotato import app - api_key = settings.get('api_key') - url_base = '/' + settings.get('url_base') if settings.get('url_base') else '' + api_key = Env.get('settings').get('api_key') + url_base = '/' + Env.get('settings').get('url_base') if Env.get('settings').get('url_base') else '' reloader = debug and not options.daemonize # Basic config - app.host = settings.get('host', default = '0.0.0.0') - app.port = settings.get('port', default = 5000) + app.host = Env.get('settings').get('host', default = '0.0.0.0') + app.port = Env.get('settings').get('port', default = 5000) app.debug = debug app.secret_key = api_key app.static_path = url_base + '/static' diff --git a/couchpotato/core/auth.py b/couchpotato/core/auth.py index 87e5820..d6b2f8a 100644 --- a/couchpotato/core/auth.py +++ b/couchpotato/core/auth.py @@ -1,9 +1,9 @@ -from couchpotato.core.settings import settings +from couchpotato.environment import Env from flask import request, Response from functools import wraps def check_auth(username, password): - return username == settings.get('username') and password == settings.get('password') + return username == Env.get('settings').get('username') and password == Env.get('settings').get('password') def authenticate(): return Response( @@ -16,7 +16,7 @@ def requires_auth(f): @wraps(f) def decorated(*args, **kwargs): auth = request.authorization - if settings.get('username') and (not auth or not check_auth(auth.username, auth.password)): + if Env.get('settings').get('username') and (not auth or not check_auth(auth.username, auth.password)): return authenticate() return f(*args, **kwargs) diff --git a/couchpotato/core/settings/__init__.py b/couchpotato/core/settings/__init__.py index 52bd00e..6a98b37 100644 --- a/couchpotato/core/settings/__init__.py +++ b/couchpotato/core/settings/__init__.py @@ -94,5 +94,3 @@ class Settings(): return True except ValueError: return False - -settings = Settings() diff --git a/couchpotato/core/settings/db.py b/couchpotato/core/settings/db.py deleted file mode 100644 index dfa2e37..0000000 --- a/couchpotato/core/settings/db.py +++ /dev/null @@ -1,64 +0,0 @@ -import sqlalchemy as sa -from sqlalchemy import orm - - -class DatabaseError(Exception): - """Custom exceptions related to the database.""" - pass - - -def _session_cls_cache(cache={}): - """Holds a dictionary to cache session objects.""" - return cache - - -def get_session(engine=None, test=False): - """ - Get the current session or create a new one based on the engine. - - >>> from couchpotato import db - >>> from sqlalchemy import create_engine - >>> engine = create_engine('sqlite:///:memory:') - >>> session = db.get_session(engine) - >>> session #doctest: +ELLIPSIS - - - Once a session has been created, get_session will return session instances - of the same Session class. - >>> type(session) == type(db.get_session()) - True - - If you create multiple sessions for different engines, you need to - specify which session you want by passing the engine explicitely. - - >>> other_engine = create_engine('sqlite:///:memory:') - >>> other_session = db.get_session(other_engine) - >>> type(other_session) is type(db.get_session(other_engine)) - True - - """ - cache = _session_cls_cache() - - assert not(engine and test), "Cannot pass both test and engine." - # It doesn't make sense to both pass an engine and instruct the function - # to create a new engine. Decide what you want to do, but not both. - if test: - in_memory = sa.create_engine('sqlite:///:memory:') - session = orm.sessionmaker(bind=in_memory)() - # create Session class ^ ^ - # create Session instance ^ - elif engine: - key = (engine, ) - if key not in cache: - cache[key] = orm.sessionmaker(bind=engine) - session = cache[key]() - elif len(cache) == 1: - session = (cache[key] for key in cache).next()() - # return the first element ^ ^ - # instantiate session ^ - elif len(cache) >= 1: - raise DatabaseError("Multiple Session classes found. Choose one.") - else: - raise DatabaseError("No session found. You need to create one.") - - return session diff --git a/couchpotato/core/settings/model.py b/couchpotato/core/settings/model.py index 37ec3d7..6949a25 100644 --- a/couchpotato/core/settings/model.py +++ b/couchpotato/core/settings/model.py @@ -1,7 +1,10 @@ -from elixir import * -from sqlalchemy import create_engine -from sqlalchemy.orm import scoped_session, sessionmaker -from sqlalchemy.schema import ThreadLocalMetaData +from elixir.entity import Entity +from elixir.fields import Field +from elixir.options import options_defaults +from elixir.relationships import OneToMany, ManyToOne +from sqlalchemy.types import Integer, String, Unicode + +options_defaults["shortnames"] = True # We would like to be able to create this schema in a specific database at # will, so we can test it easily. @@ -10,12 +13,11 @@ from sqlalchemy.schema import ThreadLocalMetaData # http://elixir.ematia.de/trac/wiki/Recipes/MultipleDatabasesOneMetadata __session__ = None - class Resource(Entity): - """Represents a resource of movies. This recources can be online or - offline.""" - name = Field(UnicodeString(255)) - path = Field(UnicodeString(255)) + """Represents a resource of movies. + This resources can be online or offline.""" + name = Field(Unicode(255)) + path = Field(Unicode(255)) releases = OneToMany('Release') @@ -30,7 +32,7 @@ class Release(Entity): class File(Entity): """File that belongs to a release.""" history = OneToMany('RenameHistory') - path = Field(UnicodeString(255), nullable = False, unique = True) + path = Field(Unicode(255), nullable = False, unique = True) # Subtitles can have multiple parts, too part = Field(Integer) release = ManyToOne('Release') @@ -42,7 +44,7 @@ class File(Entity): class FileType(Entity): """Types could be trailer, subtitle, movie, partial movie etc.""" identifier = Field(String(20), unique = True) - name = Field(UnicodeString(255), nullable = False) + name = Field(Unicode(255), nullable = False) files = OneToMany('File') diff --git a/couchpotato/environment.py b/couchpotato/environment.py new file mode 100644 index 0000000..3c45973 --- /dev/null +++ b/couchpotato/environment.py @@ -0,0 +1,24 @@ +from couchpotato.core.settings import Settings + +class Env: + _debug = False + _settings = Settings() + _options = None + _args = None + _quiet = False + + _app_dir = "" + _data_dir = "" + _db_path = "" + + @staticmethod + def doDebug(): + return Env._debug + + @staticmethod + def get(attr): + return getattr(Env, '_' + attr) + + @staticmethod + def set(attr, value): + return setattr(Env, '_' + attr, value)