Remove Flask dependencies
This commit is contained in:
1 parent
1694ed7758
commit
6e0857c6c1
157 files changed
+757
-37916
No files matched your search
+46
-46
@@ -1,84 +1,84 @@
|
||||
from couchpotato.api import api_docs, api_docs_missing
|
||||
from couchpotato.api import api_docs, api_docs_missing, api
|
||||
from couchpotato.core.auth import requires_auth
|
||||
from couchpotato.core.event import fireEvent
|
||||
from couchpotato.core.helpers.request import getParams, jsonified
|
||||
from couchpotato.core.helpers.variable import md5
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.environment import Env
|
||||
from flask.app import Flask
|
||||
from flask.blueprints import Blueprint
|
||||
from flask.globals import request
|
||||
from flask.helpers import url_for
|
||||
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
|
||||
from tornado import template
|
||||
from tornado.web import RequestHandler
|
||||
import os
|
||||
import time
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
app = Flask(__name__, static_folder = 'nope')
|
||||
web = Blueprint('web', __name__)
|
||||
views = {}
|
||||
template_loader = template.Loader(os.path.join(os.path.dirname(__file__), 'templates'))
|
||||
|
||||
# Main web handler
|
||||
@requires_auth
|
||||
class WebHandler(RequestHandler):
|
||||
def get(self, route):
|
||||
if not views.get(route):
|
||||
page_not_found(self)
|
||||
return
|
||||
self.write(views[route]())
|
||||
|
||||
def addView(route, func, static = False):
|
||||
views[route] = func
|
||||
|
||||
def get_session(engine = None):
|
||||
return Env.getSession(engine)
|
||||
|
||||
def addView(route, func, static = False):
|
||||
web.add_url_rule(route + ('' if static else '/'), endpoint = route if route else 'index', view_func = func)
|
||||
|
||||
""" Web view """
|
||||
@web.route('/')
|
||||
@requires_auth
|
||||
# Web view
|
||||
def index():
|
||||
return render_template('index.html', sep = os.sep, fireEvent = fireEvent, env = Env)
|
||||
return template_loader.load('index.html').generate(sep = os.sep, fireEvent = fireEvent, Env = Env)
|
||||
addView('', index)
|
||||
|
||||
""" Api view """
|
||||
@web.route('docs/')
|
||||
@requires_auth
|
||||
# API docs
|
||||
def apiDocs():
|
||||
from couchpotato import app
|
||||
routes = []
|
||||
for route, x in sorted(app.view_functions.iteritems()):
|
||||
if route[0:4] == 'api.':
|
||||
routes += [route[4:].replace('::', '.')]
|
||||
|
||||
for route in api.iterkeys():
|
||||
routes.append(route)
|
||||
|
||||
if api_docs.get(''):
|
||||
del api_docs['']
|
||||
del api_docs_missing['']
|
||||
return render_template('api.html', fireEvent = fireEvent, routes = sorted(routes), api_docs = api_docs, api_docs_missing = sorted(api_docs_missing))
|
||||
|
||||
@web.route('getkey/')
|
||||
def getApiKey():
|
||||
return template_loader.load('api.html').generate(fireEvent = fireEvent, routes = sorted(routes), api_docs = api_docs, api_docs_missing = sorted(api_docs_missing), Env = Env)
|
||||
|
||||
api = None
|
||||
params = getParams()
|
||||
username = Env.setting('username')
|
||||
password = Env.setting('password')
|
||||
addView('docs', apiDocs)
|
||||
|
||||
if (params.get('u') == md5(username) or not username) and (params.get('p') == password or not password):
|
||||
api = Env.setting('api_key')
|
||||
# Make non basic auth option to get api key
|
||||
class KeyHandler(RequestHandler):
|
||||
def get(self):
|
||||
api = None
|
||||
username = Env.setting('username')
|
||||
password = Env.setting('password')
|
||||
|
||||
return jsonified({
|
||||
'success': api is not None,
|
||||
'api_key': api
|
||||
})
|
||||
if (self.get_argument('u') == md5(username) or not username) and (self.get_argument('p') == password or not password):
|
||||
api = Env.setting('api_key')
|
||||
|
||||
@app.errorhandler(404)
|
||||
def page_not_found(error):
|
||||
index_url = url_for('web.index')
|
||||
url = request.path[len(index_url):]
|
||||
self.write({
|
||||
'success': api is not None,
|
||||
'api_key': api
|
||||
})
|
||||
|
||||
def page_not_found(rh):
|
||||
index_url = Env.get('web_base')
|
||||
url = rh.request.uri[len(index_url):]
|
||||
|
||||
if url[:3] != 'api':
|
||||
if request.path != '/':
|
||||
r = request.url.replace(request.path, index_url + '#' + url)
|
||||
else:
|
||||
r = '%s%s' % (request.url.rstrip('/'), index_url + '#' + url)
|
||||
return redirect(r)
|
||||
r = index_url + '#' + url.lstrip('/')
|
||||
rh.redirect(r)
|
||||
else:
|
||||
if not Env.get('dev'):
|
||||
time.sleep(0.1)
|
||||
return 'Wrong API key used', 404
|
||||
|
||||
rh.set_status(404)
|
||||
rh.write('Wrong API key used')
|
||||
|
||||
+40
-19
@@ -1,14 +1,14 @@
|
||||
from flask.blueprints import Blueprint
|
||||
from flask.helpers import url_for
|
||||
from tornado.web import RequestHandler, asynchronous
|
||||
from werkzeug.utils import redirect
|
||||
import json
|
||||
import urllib
|
||||
|
||||
api = Blueprint('api', __name__)
|
||||
api_docs = {}
|
||||
api_docs_missing = []
|
||||
api = {}
|
||||
api_nonblock = {}
|
||||
|
||||
api_docs = {}
|
||||
api_docs_missing = []
|
||||
|
||||
# NonBlock API handler
|
||||
class NonBlockHandler(RequestHandler):
|
||||
|
||||
stoppers = []
|
||||
@@ -32,14 +32,6 @@ class NonBlockHandler(RequestHandler):
|
||||
|
||||
self.stoppers = []
|
||||
|
||||
|
||||
def addApiView(route, func, static = False, docs = None, **kwargs):
|
||||
api.add_url_rule(route + ('' if static else '/'), endpoint = route.replace('.', '::') if route else 'index', view_func = func, **kwargs)
|
||||
if docs:
|
||||
api_docs[route[4:] if route[0:4] == 'api.' else route] = docs
|
||||
else:
|
||||
api_docs_missing.append(route)
|
||||
|
||||
def addNonBlockApiView(route, func_tuple, docs = None, **kwargs):
|
||||
api_nonblock[route] = func_tuple
|
||||
|
||||
@@ -48,9 +40,38 @@ def addNonBlockApiView(route, func_tuple, docs = None, **kwargs):
|
||||
else:
|
||||
api_docs_missing.append(route)
|
||||
|
||||
""" Api view """
|
||||
def index():
|
||||
index_url = url_for('web.index')
|
||||
return redirect(index_url + 'docs/')
|
||||
# Blocking API handler
|
||||
class ApiHandler(RequestHandler):
|
||||
|
||||
addApiView('', index)
|
||||
def get(self, route):
|
||||
if not api.get(route):
|
||||
self.write('API call doesn\'t seem to exist')
|
||||
|
||||
kwargs = {}
|
||||
for x in self.request.arguments:
|
||||
kwargs[x] = urllib.unquote(self.get_argument(x))
|
||||
|
||||
# Remove t random string
|
||||
try: del kwargs['t']
|
||||
except: pass
|
||||
|
||||
# Check JSONP callback
|
||||
result = api[route](**kwargs)
|
||||
jsonp_callback = self.get_argument('callback_func', default = None)
|
||||
|
||||
if jsonp_callback:
|
||||
self.write(str(jsonp_callback) + '(' + json.dumps(result) + ')')
|
||||
elif isinstance(result, (tuple)) and result[0] == 'redirect':
|
||||
self.redirect(result[1])
|
||||
else:
|
||||
self.write(result)
|
||||
|
||||
def addApiView(route, func, static = False, docs = None, **kwargs):
|
||||
|
||||
if static: func(route)
|
||||
else: api[route] = func
|
||||
|
||||
if docs:
|
||||
api_docs[route[4:] if route[0:4] == 'api.' else route] = docs
|
||||
else:
|
||||
api_docs_missing.append(route)
|
||||
@@ -1,6 +1,5 @@
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.event import fireEvent, addEvent
|
||||
from couchpotato.core.helpers.request import jsonified
|
||||
from couchpotato.core.helpers.variable import cleanHost, md5
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
@@ -69,9 +68,9 @@ class Core(Plugin):
|
||||
return True
|
||||
|
||||
def available(self):
|
||||
return jsonified({
|
||||
return {
|
||||
'success': True
|
||||
})
|
||||
}
|
||||
|
||||
def shutdown(self):
|
||||
if self.shutdown_started:
|
||||
@@ -156,10 +155,10 @@ class Core(Plugin):
|
||||
host = 'localhost'
|
||||
port = Env.setting('port')
|
||||
|
||||
return '%s:%d%s' % (cleanHost(host).rstrip('/'), int(port), '/' + Env.setting('url_base').lstrip('/') if Env.setting('url_base') else '')
|
||||
return '%s:%d%s' % (cleanHost(host).rstrip('/'), int(port), Env.get('web_base'))
|
||||
|
||||
def createApiUrl(self):
|
||||
return '%s/api/%s' % (self.createBaseUrl(), Env.setting('api_key'))
|
||||
return '%sapi/%s' % (self.createBaseUrl(), Env.setting('api_key'))
|
||||
|
||||
def version(self):
|
||||
ver = fireEvent('updater.info', single = True)
|
||||
@@ -171,9 +170,9 @@ class Core(Plugin):
|
||||
return '%s - %s-%s - v2' % (platf, ver.get('version')['type'], ver.get('version')['hash'])
|
||||
|
||||
def versionView(self):
|
||||
return jsonified({
|
||||
return {
|
||||
'version': self.version()
|
||||
})
|
||||
}
|
||||
|
||||
def signalHandler(self):
|
||||
if Env.get('daemonized'): return
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.event import addEvent, fireEvent, fireEventAsync
|
||||
from couchpotato.core.helpers.encoding import ss
|
||||
from couchpotato.core.helpers.request import jsonified
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
from couchpotato.environment import Env
|
||||
@@ -36,7 +35,7 @@ class Updater(Plugin):
|
||||
addEvent('app.load', self.setCrons)
|
||||
addEvent('updater.info', self.info)
|
||||
|
||||
addApiView('updater.info', self.getInfo, docs = {
|
||||
addApiView('updater.info', self.info, docs = {
|
||||
'desc': 'Get updater information',
|
||||
'return': {
|
||||
'type': 'object',
|
||||
@@ -92,17 +91,14 @@ class Updater(Plugin):
|
||||
|
||||
return False
|
||||
|
||||
def info(self):
|
||||
def info(self, **kwargs):
|
||||
return self.updater.info()
|
||||
|
||||
def getInfo(self):
|
||||
return jsonified(self.updater.info())
|
||||
|
||||
def checkView(self):
|
||||
return jsonified({
|
||||
return {
|
||||
'update_available': self.check(force = True),
|
||||
'info': self.updater.info()
|
||||
})
|
||||
}
|
||||
|
||||
def doUpdateView(self):
|
||||
|
||||
@@ -119,9 +115,9 @@ class Updater(Plugin):
|
||||
if not success:
|
||||
success = True
|
||||
|
||||
return jsonified({
|
||||
return {
|
||||
'success': success
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
class BaseUpdater(Plugin):
|
||||
@@ -138,9 +134,6 @@ class BaseUpdater(Plugin):
|
||||
def doUpdate(self):
|
||||
pass
|
||||
|
||||
def getInfo(self):
|
||||
return jsonified(self.info())
|
||||
|
||||
def info(self):
|
||||
return {
|
||||
'last_check': self.last_check,
|
||||
|
||||
+41
-16
@@ -1,26 +1,51 @@
|
||||
from couchpotato.core.helpers.variable import md5
|
||||
from couchpotato.environment import Env
|
||||
from flask import request, Response
|
||||
from functools import wraps
|
||||
import base64
|
||||
|
||||
def check_auth(username, password):
|
||||
return username == Env.setting('username') and password == Env.setting('password')
|
||||
|
||||
def authenticate():
|
||||
return Response(
|
||||
'This is not the page you are looking for. *waves hand*', 401,
|
||||
{'WWW-Authenticate': 'Basic realm="CouchPotato Login"'}
|
||||
)
|
||||
def requires_auth(handler_class):
|
||||
|
||||
def requires_auth(f):
|
||||
def wrap_execute(handler_execute):
|
||||
|
||||
@wraps(f)
|
||||
def decorated(*args, **kwargs):
|
||||
auth = getattr(request, 'authorization')
|
||||
if Env.setting('username') and Env.setting('password'):
|
||||
if (not auth or not check_auth(auth.username.decode('latin1'), md5(auth.password.decode('latin1').encode(Env.get('encoding'))))):
|
||||
return authenticate()
|
||||
def require_basic_auth(handler, kwargs):
|
||||
if Env.setting('username') and Env.setting('password'):
|
||||
|
||||
return f(*args, **kwargs)
|
||||
auth_header = handler.request.headers.get('Authorization')
|
||||
auth_decoded = base64.decodestring(auth_header[6:]) if auth_header else None
|
||||
if auth_decoded:
|
||||
username, password = auth_decoded.split(':', 2)
|
||||
|
||||
return decorated
|
||||
if auth_header is None or not auth_header.startswith('Basic ') or (not check_auth(username.decode('latin'), md5(password.decode('latin')))):
|
||||
handler.set_status(401)
|
||||
handler.set_header('WWW-Authenticate', 'Basic realm="CouchPotato Login"')
|
||||
handler._transforms = []
|
||||
handler.finish()
|
||||
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def _execute(self, transforms, *args, **kwargs):
|
||||
|
||||
if not require_basic_auth(self, kwargs):
|
||||
return False
|
||||
return handler_execute(self, transforms, *args, **kwargs)
|
||||
|
||||
return _execute
|
||||
|
||||
handler_class._execute = wrap_execute(handler_class._execute)
|
||||
|
||||
return handler_class
|
||||
|
||||
# @wraps(f)
|
||||
# def decorated(*args, **kwargs):
|
||||
# auth = getattr(request, 'authorization')
|
||||
# if Env.setting('username') and Env.setting('password'):
|
||||
# if (not auth or not check_auth(auth.username.decode('latin1'), md5(auth.password.decode('latin1').encode(Env.get('encoding'))))):
|
||||
# return authenticate()
|
||||
#
|
||||
# return f(*args, **kwargs)
|
||||
#
|
||||
# return decorated
|
||||
@@ -1,82 +0,0 @@
|
||||
from couchpotato.core.helpers.encoding import toUnicode
|
||||
from couchpotato.core.helpers.variable import natcmp
|
||||
from flask.globals import current_app
|
||||
from flask.helpers import json, make_response
|
||||
from urllib import unquote
|
||||
from werkzeug.urls import url_decode
|
||||
import flask
|
||||
import re
|
||||
|
||||
def getParams():
|
||||
|
||||
params = url_decode(getattr(flask.request, 'environ').get('QUERY_STRING', ''))
|
||||
reg = re.compile('^[a-z0-9_\.]+$')
|
||||
|
||||
current = temp = {}
|
||||
for param, value in sorted(params.iteritems()):
|
||||
|
||||
nest = re.split("([\[\]]+)", param)
|
||||
if len(nest) > 1:
|
||||
nested = []
|
||||
for key in nest:
|
||||
if reg.match(key):
|
||||
nested.append(key)
|
||||
|
||||
current = temp
|
||||
|
||||
for item in nested:
|
||||
if item is nested[-1]:
|
||||
current[item] = toUnicode(unquote(value))
|
||||
else:
|
||||
try:
|
||||
current[item]
|
||||
except:
|
||||
current[item] = {}
|
||||
|
||||
current = current[item]
|
||||
else:
|
||||
temp[param] = toUnicode(unquote(value))
|
||||
|
||||
return dictToList(temp)
|
||||
|
||||
def dictToList(params):
|
||||
|
||||
if type(params) is dict:
|
||||
new = {}
|
||||
for x, value in params.iteritems():
|
||||
try:
|
||||
new_value = [dictToList(value[k]) for k in sorted(value.iterkeys(), cmp = natcmp)]
|
||||
except:
|
||||
new_value = value
|
||||
|
||||
new[x] = new_value
|
||||
else:
|
||||
new = params
|
||||
|
||||
return new
|
||||
|
||||
def getParam(attr, default = None):
|
||||
try:
|
||||
return getParams().get(attr, default)
|
||||
except:
|
||||
return default
|
||||
|
||||
def padded_jsonify(callback, *args, **kwargs):
|
||||
content = str(callback) + '(' + json.dumps(dict(*args, **kwargs)) + ')'
|
||||
return getattr(current_app, 'response_class')(content, mimetype = 'text/javascript')
|
||||
|
||||
def jsonify(mimetype, *args, **kwargs):
|
||||
content = json.dumps(dict(*args, **kwargs))
|
||||
return getattr(current_app, 'response_class')(content, mimetype = mimetype)
|
||||
|
||||
def jsonified(*args, **kwargs):
|
||||
callback = getParam('callback_func', None)
|
||||
if callback:
|
||||
content = padded_jsonify(callback, *args, **kwargs)
|
||||
else:
|
||||
content = jsonify('application/json', *args, **kwargs)
|
||||
|
||||
response = make_response(content)
|
||||
response.cache_control.no_cache = True
|
||||
|
||||
return response
|
||||
@@ -1,6 +1,5 @@
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.event import addEvent
|
||||
from couchpotato.core.helpers.request import jsonified
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.providers.base import Provider
|
||||
from couchpotato.environment import Env
|
||||
@@ -62,7 +61,9 @@ class Notification(Provider):
|
||||
listener = 'test'
|
||||
)
|
||||
|
||||
return jsonified({'success': success})
|
||||
return {
|
||||
'success': success
|
||||
}
|
||||
|
||||
def testNotifyName(self):
|
||||
return 'notify.%s.test' % self.getName().lower()
|
||||
@@ -2,7 +2,6 @@ from couchpotato import get_session
|
||||
from couchpotato.api import addApiView, addNonBlockApiView
|
||||
from couchpotato.core.event import addEvent, fireEvent
|
||||
from couchpotato.core.helpers.encoding import toUnicode
|
||||
from couchpotato.core.helpers.request import jsonified, getParam
|
||||
from couchpotato.core.helpers.variable import tryInt, splitString
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.notifications.base import Notification
|
||||
@@ -62,11 +61,9 @@ class CoreNotifier(Notification):
|
||||
db.commit()
|
||||
|
||||
|
||||
def markAsRead(self):
|
||||
def markAsRead(self, ids = None):
|
||||
|
||||
ids = None
|
||||
if getParam('ids'):
|
||||
ids = splitString(getParam('ids'))
|
||||
ids = splitString(ids) if ids else None
|
||||
|
||||
db = get_session()
|
||||
|
||||
@@ -79,14 +76,13 @@ class CoreNotifier(Notification):
|
||||
|
||||
db.commit()
|
||||
|
||||
return jsonified({
|
||||
return {
|
||||
'success': True
|
||||
})
|
||||
}
|
||||
|
||||
def listView(self):
|
||||
def listView(self, limit_offset = None, **kwargs):
|
||||
|
||||
db = get_session()
|
||||
limit_offset = getParam('limit_offset', None)
|
||||
|
||||
q = db.query(Notif)
|
||||
|
||||
@@ -105,11 +101,11 @@ class CoreNotifier(Notification):
|
||||
ndict['type'] = 'notification'
|
||||
notifications.append(ndict)
|
||||
|
||||
return jsonified({
|
||||
return {
|
||||
'success': True,
|
||||
'empty': len(notifications) == 0,
|
||||
'notifications': notifications
|
||||
})
|
||||
}
|
||||
|
||||
def checkMessages(self):
|
||||
|
||||
@@ -219,12 +215,12 @@ class CoreNotifier(Notification):
|
||||
|
||||
return recent or []
|
||||
|
||||
def listener(self):
|
||||
def listener(self, init = False, **kwargs):
|
||||
|
||||
messages = []
|
||||
|
||||
# Get unread
|
||||
if getParam('init'):
|
||||
if init:
|
||||
db = get_session()
|
||||
|
||||
notifications = db.query(Notif) \
|
||||
@@ -235,7 +231,7 @@ class CoreNotifier(Notification):
|
||||
ndict['type'] = 'notification'
|
||||
messages.append(ndict)
|
||||
|
||||
return jsonified({
|
||||
return {
|
||||
'success': True,
|
||||
'result': messages,
|
||||
})
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.event import addEvent
|
||||
from couchpotato.core.helpers.encoding import tryUrlencode
|
||||
from couchpotato.core.helpers.request import getParams, jsonified
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.notifications.base import Notification
|
||||
import re
|
||||
@@ -22,10 +21,7 @@ class NMJ(Notification):
|
||||
addApiView(self.testNotifyName(), self.test)
|
||||
addApiView('notify.nmj.auto_config', self.autoConfig)
|
||||
|
||||
def autoConfig(self):
|
||||
|
||||
params = getParams()
|
||||
host = params.get('host', 'localhost')
|
||||
def autoConfig(self, host = 'localhost', **kwargs):
|
||||
|
||||
database = ''
|
||||
mount = ''
|
||||
@@ -63,11 +59,11 @@ class NMJ(Notification):
|
||||
log.error('Detected a network share on the Popcorn Hour, but could not get the mounting url')
|
||||
return self.failed()
|
||||
|
||||
return jsonified({
|
||||
return {
|
||||
'success': True,
|
||||
'database': database,
|
||||
'mount': mount,
|
||||
})
|
||||
}
|
||||
|
||||
def addToLibrary(self, message = None, group = {}):
|
||||
if self.isDisabled(): return
|
||||
@@ -113,9 +109,13 @@ class NMJ(Notification):
|
||||
return True
|
||||
|
||||
def failed(self):
|
||||
return jsonified({'success': False})
|
||||
return {
|
||||
'success': False
|
||||
}
|
||||
|
||||
def test(self):
|
||||
return jsonified({'success': self.addToLibrary()})
|
||||
return {
|
||||
'success': self.addToLibrary()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
from couchpotato.core.helpers.encoding import toUnicode
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.notifications.base import Notification
|
||||
from flask.helpers import json
|
||||
import base64
|
||||
import json
|
||||
import traceback
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
from couchpotato.core.event import addEvent
|
||||
from couchpotato.core.helpers.encoding import tryUrlencode
|
||||
from couchpotato.core.helpers.request import jsonified
|
||||
from couchpotato.core.helpers.variable import cleanHost
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.notifications.base import Notification
|
||||
@@ -86,4 +85,6 @@ class Plex(Notification):
|
||||
)
|
||||
success2 = self.addToLibrary()
|
||||
|
||||
return jsonified({'success': success or success2})
|
||||
return {
|
||||
'success': success or success2
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
from couchpotato.core.event import addEvent
|
||||
from couchpotato.core.helpers.request import jsonified
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.notifications.base import Notification
|
||||
import os
|
||||
@@ -33,4 +32,6 @@ class Synoindex(Notification):
|
||||
return True
|
||||
|
||||
def test(self):
|
||||
return jsonified({'success': os.path.isfile(self.index_path)})
|
||||
return {
|
||||
'success': os.path.isfile(self.index_path)
|
||||
}
|
||||
@@ -1,12 +1,10 @@
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.helpers.encoding import tryUrlencode
|
||||
from couchpotato.core.helpers.request import jsonified, getParam
|
||||
from couchpotato.core.helpers.variable import cleanHost
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.notifications.base import Notification
|
||||
from flask.helpers import url_for
|
||||
from couchpotato.environment import Env
|
||||
from pytwitter import Api, parse_qsl
|
||||
from werkzeug.utils import redirect
|
||||
import oauth2
|
||||
|
||||
log = CPLog(__name__)
|
||||
@@ -70,10 +68,9 @@ class Twitter(Notification):
|
||||
|
||||
return True
|
||||
|
||||
def getAuthorizationUrl(self):
|
||||
def getAuthorizationUrl(self, host = None, **kwargs):
|
||||
|
||||
referer = getParam('host')
|
||||
callback_url = cleanHost(referer) + '%snotify.%s.credentials/' % (url_for('api.index').lstrip('/'), self.getName().lower())
|
||||
callback_url = cleanHost(host) + '%snotify.%s.credentials/' % (Env.get('api_base').lstrip('/'), self.getName().lower())
|
||||
|
||||
oauth_consumer = oauth2.Consumer(self.consumer_key, self.consumer_secret)
|
||||
oauth_client = oauth2.Client(oauth_consumer)
|
||||
@@ -82,31 +79,29 @@ class Twitter(Notification):
|
||||
|
||||
if resp['status'] != '200':
|
||||
log.error('Invalid response from Twitter requesting temp token: %s', resp['status'])
|
||||
return jsonified({
|
||||
return {
|
||||
'success': False,
|
||||
})
|
||||
}
|
||||
else:
|
||||
self.request_token = dict(parse_qsl(content))
|
||||
|
||||
auth_url = self.urls['authorize'] + ("?oauth_token=%s" % self.request_token['oauth_token'])
|
||||
|
||||
log.info('Redirecting to "%s"', auth_url)
|
||||
return jsonified({
|
||||
return {
|
||||
'success': True,
|
||||
'url': auth_url,
|
||||
})
|
||||
}
|
||||
|
||||
def getCredentials(self):
|
||||
|
||||
key = getParam('oauth_verifier')
|
||||
def getCredentials(self, oauth_verifier, **kwargs):
|
||||
|
||||
token = oauth2.Token(self.request_token['oauth_token'], self.request_token['oauth_token_secret'])
|
||||
token.set_verifier(key)
|
||||
token.set_verifier(oauth_verifier)
|
||||
|
||||
oauth_consumer = oauth2.Consumer(key = self.consumer_key, secret = self.consumer_secret)
|
||||
oauth_client = oauth2.Client(oauth_consumer, token)
|
||||
|
||||
resp, content = oauth_client.request(self.urls['access'], method = 'POST', body = 'oauth_verifier=%s' % key)
|
||||
resp, content = oauth_client.request(self.urls['access'], method = 'POST', body = 'oauth_verifier=%s' % oauth_verifier)
|
||||
access_token = dict(parse_qsl(content))
|
||||
|
||||
if resp['status'] != '200':
|
||||
@@ -121,4 +116,4 @@ class Twitter(Notification):
|
||||
|
||||
self.request_token = None
|
||||
|
||||
return redirect(url_for('web.index') + 'settings/notifications/')
|
||||
return 'redirect', Env.get('web_base') + 'settings/notifications/'
|
||||
@@ -1,8 +1,8 @@
|
||||
from couchpotato.core.helpers.variable import splitString
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.notifications.base import Notification
|
||||
from flask.helpers import json
|
||||
import base64
|
||||
import json
|
||||
import traceback
|
||||
import urllib
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
from StringIO import StringIO
|
||||
from couchpotato import addView
|
||||
from couchpotato.core.event import fireEvent, addEvent
|
||||
from couchpotato.core.helpers.encoding import tryUrlencode, ss, toSafeString
|
||||
from couchpotato.core.helpers.variable import getExt, md5
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.environment import Env
|
||||
from flask.templating import render_template_string
|
||||
from multipartpost import MultipartPostHandler
|
||||
from tornado import template
|
||||
from tornado.web import StaticFileHandler
|
||||
from urlparse import urlparse
|
||||
import cookielib
|
||||
import glob
|
||||
@@ -44,35 +44,37 @@ class Plugin(object):
|
||||
def getName(self):
|
||||
return self.__class__.__name__
|
||||
|
||||
def renderTemplate(self, parent_file, template, **params):
|
||||
def renderTemplate(self, parent_file, templ, **params):
|
||||
|
||||
template = open(os.path.join(os.path.dirname(parent_file), template), 'r').read()
|
||||
return render_template_string(template, **params)
|
||||
t = template.Template(open(os.path.join(os.path.dirname(parent_file), templ), 'r').read())
|
||||
return t.generate(**params)
|
||||
|
||||
def registerStatic(self, plugin_file, add_to_head = True):
|
||||
|
||||
# Register plugin path
|
||||
self.plugin_path = os.path.dirname(plugin_file)
|
||||
static_folder = os.path.join(self.plugin_path, 'static')
|
||||
|
||||
if not os.path.isdir(static_folder):
|
||||
return
|
||||
|
||||
# Get plugin_name from PluginName
|
||||
s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', self.__class__.__name__)
|
||||
class_name = re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()
|
||||
|
||||
# View path
|
||||
path = 'api/%s/static/%s/' % (Env.setting('api_key'), class_name)
|
||||
addView(path + '<path:filename>', self.showStatic, static = True)
|
||||
|
||||
# Add handler to Tornado
|
||||
Env.get('app').add_handlers(".*$", [(Env.get('web_base') + path + '(.*)', StaticFileHandler, {'path': static_folder})])
|
||||
|
||||
# Register for HTML <HEAD>
|
||||
if add_to_head:
|
||||
for f in glob.glob(os.path.join(self.plugin_path, 'static', '*')):
|
||||
ext = getExt(f)
|
||||
if ext in ['js', 'css']:
|
||||
fireEvent('register_%s' % ('script' if ext in 'js' else 'style'), path + os.path.basename(f), f)
|
||||
|
||||
def showStatic(self, filename):
|
||||
d = os.path.join(self.plugin_path, 'static')
|
||||
|
||||
from flask.helpers import send_from_directory
|
||||
return send_from_directory(d, filename)
|
||||
|
||||
def createFile(self, path, content, binary = False):
|
||||
path = ss(path)
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.helpers.request import getParam, jsonified
|
||||
from couchpotato.core.helpers.variable import getUserDir
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
import ctypes
|
||||
@@ -63,16 +62,15 @@ class FileBrowser(Plugin):
|
||||
|
||||
return driveletters
|
||||
|
||||
def view(self):
|
||||
def view(self, path = '/', show_hidden = True, **kwargs):
|
||||
|
||||
path = getParam('path', '/')
|
||||
home = getUserDir()
|
||||
|
||||
if not path:
|
||||
path = home
|
||||
|
||||
try:
|
||||
dirs = self.getDirectories(path = path, show_hidden = getParam('show_hidden', True))
|
||||
dirs = self.getDirectories(path = path, show_hidden = show_hidden)
|
||||
except:
|
||||
dirs = []
|
||||
|
||||
@@ -82,14 +80,14 @@ class FileBrowser(Plugin):
|
||||
elif parent != '/' and parent[-2:] != ':\\':
|
||||
parent += os.path.sep
|
||||
|
||||
return jsonified({
|
||||
return {
|
||||
'is_root': path == '/',
|
||||
'empty': len(dirs) == 0,
|
||||
'parent': parent,
|
||||
'home': home + os.path.sep,
|
||||
'platform': os.name,
|
||||
'dirs': dirs,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
def is_hidden(self, filepath):
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
from couchpotato import get_session
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.event import fireEvent
|
||||
from couchpotato.core.helpers.request import jsonified, getParams
|
||||
from couchpotato.core.helpers.variable import splitString, tryInt
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
from couchpotato.core.settings.model import Movie
|
||||
from sqlalchemy.orm import joinedload_all
|
||||
import random
|
||||
import random as rndm
|
||||
import time
|
||||
|
||||
log = CPLog(__name__)
|
||||
@@ -43,14 +42,13 @@ class Dashboard(Plugin):
|
||||
|
||||
suggestions = fireEvent('movie.suggest', movies = identifiers, single = True)
|
||||
|
||||
return jsonified({
|
||||
return {
|
||||
'result': True,
|
||||
'suggestions': suggestions
|
||||
})
|
||||
}
|
||||
|
||||
def getSoonView(self):
|
||||
def getSoonView(self, limit_offset = None, random = False, late = False, **kwargs):
|
||||
|
||||
params = getParams()
|
||||
db = get_session()
|
||||
now = time.time()
|
||||
|
||||
@@ -85,7 +83,6 @@ class Dashboard(Plugin):
|
||||
.options(joinedload_all('files'))
|
||||
|
||||
# Add limit
|
||||
limit_offset = params.get('limit_offset')
|
||||
limit = 12
|
||||
if limit_offset:
|
||||
splt = splitString(limit_offset) if isinstance(limit_offset, (str, unicode)) else limit_offset
|
||||
@@ -93,8 +90,8 @@ class Dashboard(Plugin):
|
||||
|
||||
all_movies = q.all()
|
||||
|
||||
if params.get('random', False):
|
||||
random.shuffle(all_movies)
|
||||
if random:
|
||||
rndm.shuffle(all_movies)
|
||||
|
||||
movies = []
|
||||
for movie in all_movies:
|
||||
@@ -126,18 +123,18 @@ class Dashboard(Plugin):
|
||||
})
|
||||
|
||||
# Don't list older movies
|
||||
if ((not params.get('late') and (not eta.get('dvd') or (eta.get('dvd') and eta.get('dvd') > (now - 2419200)))) or \
|
||||
(params.get('late') and eta.get('dvd') and eta.get('dvd') < (now - 2419200))):
|
||||
if ((not late and (not eta.get('dvd') or (eta.get('dvd') and eta.get('dvd') > (now - 2419200)))) or \
|
||||
(late and eta.get('dvd') and eta.get('dvd') < (now - 2419200))):
|
||||
movies.append(temp)
|
||||
|
||||
if len(movies) >= limit:
|
||||
break
|
||||
|
||||
db.expire_all()
|
||||
return jsonified({
|
||||
return {
|
||||
'success': True,
|
||||
'empty': len(movies) == 0,
|
||||
'movies': movies,
|
||||
})
|
||||
}
|
||||
|
||||
getLateView = getSoonView
|
||||
@@ -2,15 +2,13 @@ from couchpotato import get_session
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.event import addEvent
|
||||
from couchpotato.core.helpers.encoding import toUnicode
|
||||
from couchpotato.core.helpers.request import jsonified
|
||||
from couchpotato.core.helpers.variable import md5, getExt
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
from couchpotato.core.plugins.scanner.main import Scanner
|
||||
from couchpotato.core.settings.model import FileType, File
|
||||
from couchpotato.environment import Env
|
||||
from flask.helpers import send_file
|
||||
from werkzeug.exceptions import NotFound
|
||||
from tornado.web import StaticFileHandler
|
||||
import os.path
|
||||
import time
|
||||
import traceback
|
||||
@@ -25,7 +23,7 @@ class FileManager(Plugin):
|
||||
addEvent('file.download', self.download)
|
||||
addEvent('file.types', self.getTypes)
|
||||
|
||||
addApiView('file.cache/<path:filename>', self.showCacheFile, static = True, docs = {
|
||||
addApiView('file.cache/(.*)', self.showCacheFile, static = True, docs = {
|
||||
'desc': 'Return a file from the cp_data/cache directory',
|
||||
'params': {
|
||||
'filename': {'desc': 'path/filename of the wanted file'}
|
||||
@@ -81,15 +79,9 @@ class FileManager(Plugin):
|
||||
except:
|
||||
log.error('Failed removing unused file: %s', traceback.format_exc())
|
||||
|
||||
def showCacheFile(self, filename = ''):
|
||||
def showCacheFile(self, route):
|
||||
Env.get('app').add_handlers(".*$", [('%s%s' % (Env.get('api_base'), route), StaticFileHandler, {'path': Env.get('cache_dir')})])
|
||||
|
||||
file_path = os.path.join(Env.get('cache_dir'), os.path.basename(filename))
|
||||
|
||||
if not os.path.isfile(file_path):
|
||||
log.error('File "%s" not found', file_path)
|
||||
raise NotFound()
|
||||
|
||||
return send_file(file_path, conditional = True)
|
||||
|
||||
def download(self, url = '', dest = None, overwrite = False, urlopen_kwargs = {}):
|
||||
|
||||
@@ -160,6 +152,6 @@ class FileManager(Plugin):
|
||||
|
||||
def getTypesView(self):
|
||||
|
||||
return jsonified({
|
||||
return {
|
||||
'types': self.getTypes()
|
||||
})
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.helpers.encoding import toUnicode
|
||||
from couchpotato.core.helpers.request import jsonified, getParam, getParams
|
||||
from couchpotato.core.helpers.variable import tryInt
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
@@ -47,9 +46,9 @@ class Logging(Plugin):
|
||||
}
|
||||
})
|
||||
|
||||
def get(self):
|
||||
def get(self, nr = 0, **kwargs):
|
||||
|
||||
nr = int(getParam('nr', 0))
|
||||
nr = tryInt(nr)
|
||||
current_path = None
|
||||
|
||||
total = 1
|
||||
@@ -71,16 +70,15 @@ class Logging(Plugin):
|
||||
f = open(current_path, 'r')
|
||||
log = f.read()
|
||||
|
||||
return jsonified({
|
||||
return {
|
||||
'success': True,
|
||||
'log': toUnicode(log),
|
||||
'total': total,
|
||||
})
|
||||
}
|
||||
|
||||
def partial(self):
|
||||
def partial(self, type = 'all', lines = 30, **kwargs):
|
||||
|
||||
log_type = getParam('type', 'all')
|
||||
total_lines = tryInt(getParam('lines', 30))
|
||||
total_lines = tryInt(lines)
|
||||
|
||||
log_lines = []
|
||||
|
||||
@@ -100,7 +98,7 @@ class Logging(Plugin):
|
||||
brk = False
|
||||
for line in reversed_lines:
|
||||
|
||||
if log_type == 'all' or '%s ' % log_type.upper() in line:
|
||||
if type == 'all' or '%s ' % type.upper() in line:
|
||||
log_lines.append(line)
|
||||
|
||||
if len(log_lines) >= total_lines:
|
||||
@@ -111,10 +109,10 @@ class Logging(Plugin):
|
||||
break
|
||||
|
||||
log_lines.reverse()
|
||||
return jsonified({
|
||||
return {
|
||||
'success': True,
|
||||
'log': '[0m\n'.join(log_lines),
|
||||
})
|
||||
}
|
||||
|
||||
def clear(self):
|
||||
|
||||
@@ -135,24 +133,21 @@ class Logging(Plugin):
|
||||
except:
|
||||
log.error('Couldn\'t delete file "%s": %s', (path, traceback.format_exc()))
|
||||
|
||||
return jsonified({
|
||||
return {
|
||||
'success': True
|
||||
})
|
||||
}
|
||||
|
||||
def log(self):
|
||||
|
||||
params = getParams()
|
||||
def log(self, type = 'error', **kwargs):
|
||||
|
||||
try:
|
||||
log_message = 'API log: %s' % params
|
||||
log_message = 'API log: %s' % kwargs
|
||||
try:
|
||||
getattr(log, params.get('type', 'error'))(log_message)
|
||||
getattr(log, type)(log_message)
|
||||
except:
|
||||
log.error(log_message)
|
||||
except:
|
||||
log.error('Couldn\'t log via API: %s', params)
|
||||
log.error('Couldn\'t log via API: %s', kwargs)
|
||||
|
||||
|
||||
return jsonified({
|
||||
return {
|
||||
'success': True
|
||||
})
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.event import fireEvent, addEvent, fireEventAsync
|
||||
from couchpotato.core.helpers.encoding import ss
|
||||
from couchpotato.core.helpers.request import jsonified, getParam
|
||||
from couchpotato.core.helpers.variable import splitString, getTitle
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
@@ -51,18 +50,17 @@ class Manage(Plugin):
|
||||
addEvent('app.load', updateLibrary)
|
||||
|
||||
def getProgress(self):
|
||||
return jsonified({
|
||||
return {
|
||||
'progress': self.in_progress
|
||||
})
|
||||
}
|
||||
|
||||
def updateLibraryView(self):
|
||||
def updateLibraryView(self, full = 1, **kwargs):
|
||||
|
||||
full = getParam('full', default = 1)
|
||||
fireEventAsync('manage.update', full = True if full == '1' else False)
|
||||
|
||||
return jsonified({
|
||||
return {
|
||||
'success': True
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
def updateLibrary(self, full = True):
|
||||
|
||||
@@ -2,7 +2,6 @@ from couchpotato import get_session
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.event import fireEvent, fireEventAsync, addEvent
|
||||
from couchpotato.core.helpers.encoding import toUnicode, simplifyString
|
||||
from couchpotato.core.helpers.request import getParams, jsonified, getParam
|
||||
from couchpotato.core.helpers.variable import getImdb, splitString
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
@@ -125,15 +124,14 @@ class MoviePlugin(Plugin):
|
||||
|
||||
db.expire_all()
|
||||
|
||||
def getView(self):
|
||||
def getView(self, id = None):
|
||||
|
||||
movie_id = getParam('id')
|
||||
movie = self.get(movie_id) if movie_id else None
|
||||
movie = self.get(id) if id else None
|
||||
|
||||
return jsonified({
|
||||
return {
|
||||
'success': movie is not None,
|
||||
'movie': movie,
|
||||
})
|
||||
}
|
||||
|
||||
def get(self, movie_id):
|
||||
|
||||
@@ -263,15 +261,14 @@ class MoviePlugin(Plugin):
|
||||
db.expire_all()
|
||||
return ''.join(sorted(chars, key = str.lower))
|
||||
|
||||
def listView(self):
|
||||
def listView(self, **kwargs):
|
||||
|
||||
params = getParams()
|
||||
status = splitString(params.get('status', None))
|
||||
release_status = splitString(params.get('release_status', None))
|
||||
limit_offset = params.get('limit_offset', None)
|
||||
starts_with = params.get('starts_with', None)
|
||||
search = params.get('search', None)
|
||||
order = params.get('order', None)
|
||||
status = splitString(kwargs.get('status', None))
|
||||
release_status = splitString(kwargs.get('release_status', None))
|
||||
limit_offset = kwargs.get('limit_offset', None)
|
||||
starts_with = kwargs.get('starts_with', None)
|
||||
search = kwargs.get('search', None)
|
||||
order = kwargs.get('order', None)
|
||||
|
||||
total_movies, movies = self.list(
|
||||
status = status,
|
||||
@@ -282,32 +279,31 @@ class MoviePlugin(Plugin):
|
||||
order = order
|
||||
)
|
||||
|
||||
return jsonified({
|
||||
return {
|
||||
'success': True,
|
||||
'empty': len(movies) == 0,
|
||||
'total': total_movies,
|
||||
'movies': movies,
|
||||
})
|
||||
}
|
||||
|
||||
def charView(self):
|
||||
def charView(self, **kwargs):
|
||||
|
||||
params = getParams()
|
||||
status = splitString(params.get('status', None))
|
||||
release_status = splitString(params.get('release_status', None))
|
||||
status = splitString(kwargs.get('status', None))
|
||||
release_status = splitString(kwargs.get('release_status', None))
|
||||
chars = self.availableChars(status, release_status)
|
||||
|
||||
return jsonified({
|
||||
return {
|
||||
'success': True,
|
||||
'empty': len(chars) == 0,
|
||||
'chars': chars,
|
||||
})
|
||||
}
|
||||
|
||||
def refresh(self):
|
||||
def refresh(self, id = ''):
|
||||
|
||||
db = get_session()
|
||||
|
||||
for id in splitString(getParam('id')):
|
||||
movie = db.query(Movie).filter_by(id = id).first()
|
||||
for x in splitString(id):
|
||||
movie = db.query(Movie).filter_by(id = x).first()
|
||||
|
||||
if movie:
|
||||
|
||||
@@ -316,17 +312,16 @@ class MoviePlugin(Plugin):
|
||||
for title in movie.library.titles:
|
||||
if title.default: default_title = title.title
|
||||
|
||||
fireEvent('notify.frontend', type = 'movie.busy.%s' % id, data = True)
|
||||
fireEventAsync('library.update', identifier = movie.library.identifier, default_title = default_title, force = True, on_complete = self.createOnComplete(id))
|
||||
fireEvent('notify.frontend', type = 'movie.busy.%s' % x, data = True)
|
||||
fireEventAsync('library.update', identifier = movie.library.identifier, default_title = default_title, force = True, on_complete = self.createOnComplete(x))
|
||||
|
||||
db.expire_all()
|
||||
return jsonified({
|
||||
return {
|
||||
'success': True,
|
||||
})
|
||||
}
|
||||
|
||||
def search(self):
|
||||
def search(self, q = ''):
|
||||
|
||||
q = getParam('q')
|
||||
cache_key = u'%s/%s' % (__name__, simplifyString(q))
|
||||
movies = Env.get('cache').get(cache_key)
|
||||
|
||||
@@ -338,11 +333,11 @@ class MoviePlugin(Plugin):
|
||||
movies = fireEvent('movie.search', q = q, merge = True)
|
||||
Env.get('cache').set(cache_key, movies)
|
||||
|
||||
return jsonified({
|
||||
return {
|
||||
'success': True,
|
||||
'empty': len(movies) == 0 if movies else 0,
|
||||
'movies': movies,
|
||||
})
|
||||
}
|
||||
|
||||
def add(self, params = {}, force_readd = True, search_after = True, update_library = False, status_id = None):
|
||||
|
||||
@@ -432,33 +427,30 @@ class MoviePlugin(Plugin):
|
||||
return movie_dict
|
||||
|
||||
|
||||
def addView(self):
|
||||
def addView(self, **kwargs):
|
||||
|
||||
params = getParams()
|
||||
movie_dict = self.add(params = kwargs)
|
||||
|
||||
movie_dict = self.add(params)
|
||||
|
||||
return jsonified({
|
||||
return {
|
||||
'success': True,
|
||||
'added': True if movie_dict else False,
|
||||
'movie': movie_dict,
|
||||
})
|
||||
}
|
||||
|
||||
def edit(self):
|
||||
def edit(self, id = '', **kwargs):
|
||||
|
||||
params = getParams()
|
||||
db = get_session()
|
||||
|
||||
available_status = fireEvent('status.get', 'available', single = True)
|
||||
|
||||
ids = splitString(params.get('id'))
|
||||
ids = splitString(id)
|
||||
for movie_id in ids:
|
||||
|
||||
m = db.query(Movie).filter_by(id = movie_id).first()
|
||||
if not m:
|
||||
continue
|
||||
|
||||
m.profile_id = params.get('profile_id')
|
||||
m.profile_id = kwargs.get('profile_id')
|
||||
|
||||
# Remove releases
|
||||
for rel in m.releases:
|
||||
@@ -467,9 +459,9 @@ class MoviePlugin(Plugin):
|
||||
db.commit()
|
||||
|
||||
# Default title
|
||||
if params.get('default_title'):
|
||||
if kwargs.get('default_title'):
|
||||
for title in m.library.titles:
|
||||
title.default = toUnicode(params.get('default_title', '')).lower() == toUnicode(title.title).lower()
|
||||
title.default = toUnicode(kwargs.get('default_title', '')).lower() == toUnicode(title.title).lower()
|
||||
|
||||
db.commit()
|
||||
|
||||
@@ -479,21 +471,19 @@ class MoviePlugin(Plugin):
|
||||
fireEventAsync('searcher.single', movie_dict, on_complete = self.createNotifyFront(movie_id))
|
||||
|
||||
db.expire_all()
|
||||
return jsonified({
|
||||
return {
|
||||
'success': True,
|
||||
})
|
||||
}
|
||||
|
||||
def deleteView(self):
|
||||
def deleteView(self, id = '', **kwargs):
|
||||
|
||||
params = getParams()
|
||||
|
||||
ids = splitString(params.get('id'))
|
||||
ids = splitString(id)
|
||||
for movie_id in ids:
|
||||
self.delete(movie_id, delete_from = params.get('delete_from', 'all'))
|
||||
self.delete(movie_id, delete_from = kwargs.get('delete_from', 'all'))
|
||||
|
||||
return jsonified({
|
||||
return {
|
||||
'success': True,
|
||||
})
|
||||
}
|
||||
|
||||
def delete(self, movie_id, delete_from = None):
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ from couchpotato import get_session
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.event import addEvent, fireEvent
|
||||
from couchpotato.core.helpers.encoding import toUnicode
|
||||
from couchpotato.core.helpers.request import jsonified, getParams, getParam
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
from couchpotato.core.settings.model import Profile, ProfileType, Movie
|
||||
@@ -48,10 +47,10 @@ class ProfilePlugin(Plugin):
|
||||
|
||||
def allView(self):
|
||||
|
||||
return jsonified({
|
||||
return {
|
||||
'success': True,
|
||||
'list': self.all()
|
||||
})
|
||||
}
|
||||
|
||||
def all(self):
|
||||
|
||||
@@ -65,30 +64,28 @@ class ProfilePlugin(Plugin):
|
||||
db.expire_all()
|
||||
return temp
|
||||
|
||||
def save(self):
|
||||
|
||||
params = getParams()
|
||||
def save(self, **kwargs):
|
||||
|
||||
db = get_session()
|
||||
|
||||
p = db.query(Profile).filter_by(id = params.get('id')).first()
|
||||
p = db.query(Profile).filter_by(id = kwargs.get('id')).first()
|
||||
if not p:
|
||||
p = Profile()
|
||||
db.add(p)
|
||||
|
||||
p.label = toUnicode(params.get('label'))
|
||||
p.order = params.get('order', p.order if p.order else 0)
|
||||
p.core = params.get('core', False)
|
||||
p.label = toUnicode(kwargs.get('label'))
|
||||
p.order = kwargs.get('order', p.order if p.order else 0)
|
||||
p.core = kwargs.get('core', False)
|
||||
|
||||
#delete old types
|
||||
[db.delete(t) for t in p.types]
|
||||
|
||||
order = 0
|
||||
for type in params.get('types', []):
|
||||
for type in kwargs.get('types', []):
|
||||
t = ProfileType(
|
||||
order = order,
|
||||
finish = type.get('finish') if order > 0 else 1,
|
||||
wait_for = params.get('wait_for'),
|
||||
wait_for = kwargs.get('wait_for'),
|
||||
quality_id = type.get('quality_id')
|
||||
)
|
||||
p.types.append(t)
|
||||
@@ -99,10 +96,10 @@ class ProfilePlugin(Plugin):
|
||||
|
||||
profile_dict = p.to_dict(self.to_dict)
|
||||
|
||||
return jsonified({
|
||||
return {
|
||||
'success': True,
|
||||
'profile': profile_dict
|
||||
})
|
||||
}
|
||||
|
||||
def default(self):
|
||||
|
||||
@@ -113,28 +110,25 @@ class ProfilePlugin(Plugin):
|
||||
db.expire_all()
|
||||
return default_dict
|
||||
|
||||
def saveOrder(self):
|
||||
def saveOrder(self, **kwargs):
|
||||
|
||||
params = getParams()
|
||||
db = get_session()
|
||||
|
||||
order = 0
|
||||
for profile in params.get('ids', []):
|
||||
for profile in kwargs.get('ids', []):
|
||||
p = db.query(Profile).filter_by(id = profile).first()
|
||||
p.hide = params.get('hidden')[order]
|
||||
p.hide = kwargs.get('hidden')[order]
|
||||
p.order = order
|
||||
|
||||
order += 1
|
||||
|
||||
db.commit()
|
||||
|
||||
return jsonified({
|
||||
return {
|
||||
'success': True
|
||||
})
|
||||
}
|
||||
|
||||
def delete(self):
|
||||
|
||||
id = getParam('id')
|
||||
def delete(self, id = None):
|
||||
|
||||
db = get_session()
|
||||
|
||||
@@ -154,10 +148,10 @@ class ProfilePlugin(Plugin):
|
||||
message = log.error('Failed deleting Profile: %s', e)
|
||||
|
||||
db.expire_all()
|
||||
return jsonified({
|
||||
return {
|
||||
'success': success,
|
||||
'message': message
|
||||
})
|
||||
}
|
||||
|
||||
def fill(self):
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ from couchpotato import get_session
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.event import addEvent
|
||||
from couchpotato.core.helpers.encoding import toUnicode
|
||||
from couchpotato.core.helpers.request import jsonified, getParams
|
||||
from couchpotato.core.helpers.variable import mergeDicts, md5, getExt
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
@@ -53,10 +52,10 @@ class QualityPlugin(Plugin):
|
||||
|
||||
def allView(self):
|
||||
|
||||
return jsonified({
|
||||
return {
|
||||
'success': True,
|
||||
'list': self.all()
|
||||
})
|
||||
}
|
||||
|
||||
def all(self):
|
||||
|
||||
@@ -88,20 +87,18 @@ class QualityPlugin(Plugin):
|
||||
if identifier == q.get('identifier'):
|
||||
return q
|
||||
|
||||
def saveSize(self):
|
||||
|
||||
params = getParams()
|
||||
def saveSize(self, **kwargs):
|
||||
|
||||
db = get_session()
|
||||
quality = db.query(Quality).filter_by(identifier = params.get('identifier')).first()
|
||||
quality = db.query(Quality).filter_by(identifier = kwargs.get('identifier')).first()
|
||||
|
||||
if quality:
|
||||
setattr(quality, params.get('value_type'), params.get('value'))
|
||||
setattr(quality, kwargs.get('value_type'), kwargs.get('value'))
|
||||
db.commit()
|
||||
|
||||
return jsonified({
|
||||
return {
|
||||
'success': True
|
||||
})
|
||||
}
|
||||
|
||||
def fill(self):
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ from couchpotato import get_session
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.event import fireEvent, addEvent
|
||||
from couchpotato.core.helpers.encoding import ss
|
||||
from couchpotato.core.helpers.request import getParam, jsonified
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
from couchpotato.core.plugins.scanner.main import Scanner
|
||||
@@ -108,13 +107,11 @@ class Release(Plugin):
|
||||
# Check database and update/insert if necessary
|
||||
return fireEvent('file.add', path = filepath, part = fireEvent('scanner.partnumber', file, single = True), type_tuple = Scanner.file_types.get(type), properties = properties, single = True)
|
||||
|
||||
def deleteView(self):
|
||||
def deleteView(self, id = None, **kwargs):
|
||||
|
||||
release_id = getParam('id')
|
||||
|
||||
return jsonified({
|
||||
'success': self.delete(release_id)
|
||||
})
|
||||
return {
|
||||
'success': self.delete(id)
|
||||
}
|
||||
|
||||
def delete(self, id):
|
||||
|
||||
@@ -146,10 +143,9 @@ class Release(Plugin):
|
||||
|
||||
return False
|
||||
|
||||
def ignore(self):
|
||||
def ignore(self, id = None, **kwargs):
|
||||
|
||||
db = get_session()
|
||||
id = getParam('id')
|
||||
|
||||
rel = db.query(Relea).filter_by(id = id).first()
|
||||
if rel:
|
||||
@@ -157,14 +153,13 @@ class Release(Plugin):
|
||||
rel.status_id = available_status.get('id') if rel.status_id is ignored_status.get('id') else ignored_status.get('id')
|
||||
db.commit()
|
||||
|
||||
return jsonified({
|
||||
return {
|
||||
'success': True
|
||||
})
|
||||
}
|
||||
|
||||
def download(self):
|
||||
def download(self, id = None, **kwargs):
|
||||
|
||||
db = get_session()
|
||||
id = getParam('id')
|
||||
|
||||
snatched_status, done_status = fireEvent('status.get', ['snatched', 'done'], single = True)
|
||||
|
||||
@@ -199,12 +194,12 @@ class Release(Plugin):
|
||||
|
||||
fireEvent('notify.frontend', type = 'release.download', data = True, message = 'Successfully snatched "%s"' % item['name'])
|
||||
|
||||
return jsonified({
|
||||
return {
|
||||
'success': success
|
||||
})
|
||||
}
|
||||
else:
|
||||
log.error('Couldn\'t find release with id: %s', id)
|
||||
|
||||
return jsonified({
|
||||
return {
|
||||
'success': False
|
||||
})
|
||||
}
|
||||
@@ -2,7 +2,6 @@ from couchpotato import get_session
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.event import addEvent, fireEvent, fireEventAsync
|
||||
from couchpotato.core.helpers.encoding import toUnicode, ss
|
||||
from couchpotato.core.helpers.request import getParams, jsonified
|
||||
from couchpotato.core.helpers.variable import getExt, mergeDicts, getTitle, \
|
||||
getImdb, link, symlink, tryInt
|
||||
from couchpotato.core.logger import CPLog
|
||||
@@ -59,13 +58,12 @@ class Renamer(Plugin):
|
||||
|
||||
return True
|
||||
|
||||
def scanView(self):
|
||||
def scanView(self, **kwargs):
|
||||
|
||||
params = getParams()
|
||||
async = tryInt(params.get('async', None))
|
||||
movie_folder = params.get('movie_folder', None)
|
||||
downloader = params.get('downloader', None)
|
||||
download_id = params.get('download_id', None)
|
||||
async = tryInt(kwargs.get('async', None))
|
||||
movie_folder = kwargs.get('movie_folder', None)
|
||||
downloader = kwargs.get('downloader', None)
|
||||
download_id = kwargs.get('download_id', None)
|
||||
|
||||
fire_handle = fireEvent if not async else fireEventAsync
|
||||
|
||||
@@ -74,9 +72,9 @@ class Renamer(Plugin):
|
||||
download_info = {'id': download_id, 'downloader': downloader} if download_id else None
|
||||
)
|
||||
|
||||
return jsonified({
|
||||
return {
|
||||
'success': True
|
||||
})
|
||||
}
|
||||
|
||||
def scan(self, movie_folder = None, download_info = None):
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ from couchpotato import get_session
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.event import addEvent, fireEvent, fireEventAsync
|
||||
from couchpotato.core.helpers.encoding import simplifyString, toUnicode
|
||||
from couchpotato.core.helpers.request import jsonified, getParam
|
||||
from couchpotato.core.helpers.variable import md5, getTitle, splitString, \
|
||||
possibleTitles
|
||||
from couchpotato.core.logger import CPLog
|
||||
@@ -67,15 +66,15 @@ class Searcher(Plugin):
|
||||
else:
|
||||
fireEvent('notify.frontend', type = 'searcher.already_started', data = True, message = 'Full search already in progress')
|
||||
|
||||
return jsonified({
|
||||
return {
|
||||
'success': not in_progress
|
||||
})
|
||||
}
|
||||
|
||||
def getProgress(self):
|
||||
|
||||
return jsonified({
|
||||
return {
|
||||
'progress': self.in_progress
|
||||
})
|
||||
}
|
||||
|
||||
def allMovies(self):
|
||||
|
||||
@@ -586,13 +585,13 @@ class Searcher(Plugin):
|
||||
|
||||
return False
|
||||
|
||||
def tryNextReleaseView(self):
|
||||
def tryNextReleaseView(self, id = None, **kwargs):
|
||||
|
||||
trynext = self.tryNextRelease(getParam('id'))
|
||||
trynext = self.tryNextRelease(id)
|
||||
|
||||
return jsonified({
|
||||
return {
|
||||
'success': trynext
|
||||
})
|
||||
}
|
||||
|
||||
def tryNextRelease(self, movie_id, manual = False):
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ from couchpotato import get_session
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.event import addEvent
|
||||
from couchpotato.core.helpers.encoding import toUnicode
|
||||
from couchpotato.core.helpers.request import jsonified
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
from couchpotato.core.settings.model import Status
|
||||
@@ -44,10 +43,10 @@ class StatusPlugin(Plugin):
|
||||
|
||||
def list(self):
|
||||
|
||||
return jsonified({
|
||||
return {
|
||||
'success': True,
|
||||
'list': self.all()
|
||||
})
|
||||
}
|
||||
|
||||
def getById(self, id):
|
||||
db = get_session()
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.event import fireEvent
|
||||
from couchpotato.core.helpers.request import jsonified, getParam
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
|
||||
class Suggestion(Plugin):
|
||||
@@ -9,14 +8,13 @@ class Suggestion(Plugin):
|
||||
|
||||
addApiView('suggestion.view', self.getView)
|
||||
|
||||
def getView(self):
|
||||
def getView(self, limit_offset = None, **kwargs):
|
||||
|
||||
limit_offset = getParam('limit_offset', None)
|
||||
total_movies, movies = fireEvent('movie.list', status = 'suggest', limit_offset = limit_offset, single = True)
|
||||
|
||||
return jsonified({
|
||||
return {
|
||||
'success': True,
|
||||
'empty': len(movies) == 0,
|
||||
'total': total_movies,
|
||||
'movies': movies,
|
||||
})
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
var includes = {{includes|tojson}};
|
||||
var excludes = {{excludes|tojson}};
|
||||
{% autoescape None %}
|
||||
|
||||
var includes = {{ json_encode(includes) }};
|
||||
var excludes = {{ json_encode(excludes) }};
|
||||
|
||||
var specialChars = '\\{}+.():-|^$';
|
||||
var makeRegex = function(pattern) {
|
||||
@@ -20,6 +22,8 @@ var makeRegex = function(pattern) {
|
||||
|
||||
var isCorrectUrl = function() {
|
||||
for(i in includes) {
|
||||
if(!includes.hasOwnProperty(i)) continue;
|
||||
|
||||
var reg = includes[i]
|
||||
if (makeRegex(reg).test(document.location.href))
|
||||
return true;
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
from couchpotato import index
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.event import fireEvent, addEvent
|
||||
from couchpotato.core.helpers.request import getParam, jsonified
|
||||
from couchpotato.core.helpers.variable import isDict
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
from couchpotato.environment import Env
|
||||
from flask.globals import request
|
||||
from flask.helpers import url_for
|
||||
from flask.templating import render_template
|
||||
from tornado.web import RequestHandler
|
||||
import os
|
||||
|
||||
log = CPLog(__name__)
|
||||
@@ -18,7 +16,8 @@ class Userscript(Plugin):
|
||||
version = 3
|
||||
|
||||
def __init__(self):
|
||||
addApiView('userscript.get/<random>/<path:filename>', self.getUserScript, static = True)
|
||||
addApiView('userscript.get/(.*)/(.*)', self.getUserScript, static = True)
|
||||
|
||||
addApiView('userscript', self.iFrame)
|
||||
addApiView('userscript.add_via_url', self.getViaUrl)
|
||||
addApiView('userscript.includes', self.getIncludes)
|
||||
@@ -26,38 +25,46 @@ class Userscript(Plugin):
|
||||
|
||||
addEvent('userscript.get_version', self.getVersion)
|
||||
|
||||
def bookmark(self):
|
||||
def bookmark(self, host = None, **kwargs):
|
||||
|
||||
params = {
|
||||
'includes': fireEvent('userscript.get_includes', merge = True),
|
||||
'excludes': fireEvent('userscript.get_excludes', merge = True),
|
||||
'host': getParam('host', None),
|
||||
'host': host,
|
||||
}
|
||||
|
||||
return self.renderTemplate(__file__, 'bookmark.js', **params)
|
||||
|
||||
def getIncludes(self):
|
||||
|
||||
return jsonified({
|
||||
return {
|
||||
'includes': fireEvent('userscript.get_includes', merge = True),
|
||||
'excludes': fireEvent('userscript.get_excludes', merge = True),
|
||||
})
|
||||
|
||||
def getUserScript(self, random = '', filename = ''):
|
||||
|
||||
params = {
|
||||
'includes': fireEvent('userscript.get_includes', merge = True),
|
||||
'excludes': fireEvent('userscript.get_excludes', merge = True),
|
||||
'version': self.getVersion(),
|
||||
'api': '%suserscript/' % url_for('api.index').lstrip('/'),
|
||||
'host': request.host_url,
|
||||
}
|
||||
|
||||
script = self.renderTemplate(__file__, 'template.js', **params)
|
||||
self.createFile(os.path.join(Env.get('cache_dir'), 'couchpotato.user.js'), script)
|
||||
def getUserScript(self, route):
|
||||
|
||||
klass = self
|
||||
|
||||
class UserscriptHandler(RequestHandler):
|
||||
|
||||
def get(self, random, route):
|
||||
|
||||
params = {
|
||||
'includes': fireEvent('userscript.get_includes', merge = True),
|
||||
'excludes': fireEvent('userscript.get_excludes', merge = True),
|
||||
'version': klass.getVersion(),
|
||||
'api': '%suserscript/' % Env.get('api_base'),
|
||||
'host': '%s://%s' % (self.request.protocol, self.request.host),
|
||||
}
|
||||
|
||||
script = klass.renderTemplate(__file__, 'template.js', **params)
|
||||
klass.createFile(os.path.join(Env.get('cache_dir'), 'couchpotato.user.js'), script)
|
||||
|
||||
self.redirect(Env.get('api_base') + 'file.cache/couchpotato.user.js')
|
||||
|
||||
Env.get('app').add_handlers(".*$", [('%s%s' % (Env.get('api_base'), route), UserscriptHandler)])
|
||||
|
||||
from flask.helpers import send_from_directory
|
||||
return send_from_directory(Env.get('cache_dir'), 'couchpotato.user.js')
|
||||
|
||||
def getVersion(self):
|
||||
|
||||
@@ -69,12 +76,12 @@ class Userscript(Plugin):
|
||||
|
||||
return version
|
||||
|
||||
def iFrame(self):
|
||||
return render_template('index.html', sep = os.sep, fireEvent = fireEvent, env = Env)
|
||||
def iFrame(self, **kwargs):
|
||||
return index()
|
||||
|
||||
def getViaUrl(self):
|
||||
def getViaUrl(self, url = None, **kwargs):
|
||||
|
||||
url = getParam('url')
|
||||
print url
|
||||
|
||||
params = {
|
||||
'url': url,
|
||||
@@ -84,4 +91,4 @@ class Userscript(Plugin):
|
||||
log.error('Failed adding movie via url: %s', url)
|
||||
params['error'] = params['movie'] if params['movie'] else 'Failed getting movie info'
|
||||
|
||||
return jsonified(params)
|
||||
return params
|
||||
@@ -9,15 +9,16 @@
|
||||
// @grant none
|
||||
// @version {{version}}
|
||||
|
||||
// @match {{host}}*
|
||||
// @match {{host}}/*
|
||||
{% for include in includes %}
|
||||
// @match {{include}}{% endfor %}
|
||||
// @match {{include}}{% end %}
|
||||
{% for exclude in excludes %}
|
||||
// @exclude {{exclude}}{% endfor %}
|
||||
// @exclude {{exclude}}{% end %}
|
||||
// @exclude {{host}}{{api.rstrip('/')}}*
|
||||
|
||||
// ==/UserScript==
|
||||
|
||||
{% autoescape None %}
|
||||
if (window.top == window.self){ // Only run on top window
|
||||
|
||||
var version = {{version}},
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
from .main import V1Importer
|
||||
|
||||
def start():
|
||||
return V1Importer()
|
||||
|
||||
config = []
|
||||
@@ -1,30 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="{{ url_for('web.static', filename='style/main.css') }}" type="text/css">
|
||||
<link rel="stylesheet" href="{{ url_for('web.static', filename='style/uniform.generic.css') }}" type="text/css">
|
||||
<link rel="stylesheet" href="{{ url_for('web.static', filename='style/uniform.css') }}" type="text/css">
|
||||
|
||||
<script type="text/javascript" src="{{ url_for('web.static', filename='scripts/library/mootools.js') }}"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
window.addEvent('domready', function(){
|
||||
if($('old_db'))
|
||||
$('old_db').addEvent('change', function(){
|
||||
$('form').submit();
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
{% if message: %}
|
||||
{{ message }}
|
||||
{% else: %}
|
||||
<form id="form" method="post" enctype="multipart/form-data">
|
||||
<input type="file" name="old_db" id="old_db" />
|
||||
</form>
|
||||
{% endif %}
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,56 +0,0 @@
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.event import fireEventAsync
|
||||
from couchpotato.core.helpers.variable import getImdb
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
from couchpotato.environment import Env
|
||||
from flask.globals import request
|
||||
from flask.helpers import url_for
|
||||
import os
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
|
||||
class V1Importer(Plugin):
|
||||
|
||||
def __init__(self):
|
||||
addApiView('v1.import', self.fromOld, methods = ['GET', 'POST'])
|
||||
|
||||
def fromOld(self):
|
||||
|
||||
if request.method != 'POST':
|
||||
return self.renderTemplate(__file__, 'form.html', url_for = url_for)
|
||||
|
||||
file = request.files['old_db']
|
||||
|
||||
uploaded_file = os.path.join(Env.get('cache_dir'), 'v1_database.db')
|
||||
|
||||
if os.path.isfile(uploaded_file):
|
||||
os.remove(uploaded_file)
|
||||
|
||||
file.save(uploaded_file)
|
||||
|
||||
try:
|
||||
import sqlite3
|
||||
conn = sqlite3.connect(uploaded_file)
|
||||
|
||||
wanted = []
|
||||
|
||||
t = ('want',)
|
||||
cur = conn.execute('SELECT status, imdb FROM Movie WHERE status=?', t)
|
||||
for row in cur:
|
||||
status, imdb = row
|
||||
if getImdb(imdb):
|
||||
wanted.append(imdb)
|
||||
conn.close()
|
||||
|
||||
wanted = set(wanted)
|
||||
for imdb in wanted:
|
||||
fireEventAsync('movie.add', {'identifier': imdb}, search_after = False)
|
||||
|
||||
message = 'Successfully imported %s movie(s)' % len(wanted)
|
||||
except Exception, e:
|
||||
message = 'Failed: %s' % e
|
||||
|
||||
return self.renderTemplate(__file__, 'form.html', url_for = url_for, message = message)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
from couchpotato import get_session
|
||||
from couchpotato.core.event import addEvent, fireEvent
|
||||
from couchpotato.core.helpers.encoding import tryUrlencode
|
||||
from couchpotato.core.helpers.request import jsonified, getParams
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.providers.movie.base import MovieProvider
|
||||
from couchpotato.core.settings.model import Movie
|
||||
@@ -92,11 +91,10 @@ class CouchPotatoApi(MovieProvider):
|
||||
|
||||
return suggestions
|
||||
|
||||
def suggestView(self):
|
||||
def suggestView(self, **kwargs):
|
||||
|
||||
params = getParams()
|
||||
movies = params.get('movies')
|
||||
ignore = params.get('ignore', [])
|
||||
movies = kwargs.get('movies')
|
||||
ignore = kwargs.get('ignore', [])
|
||||
|
||||
if not movies:
|
||||
db = get_session()
|
||||
@@ -105,11 +103,11 @@ class CouchPotatoApi(MovieProvider):
|
||||
|
||||
suggestions = self.suggest(movies, ignore)
|
||||
|
||||
return jsonified({
|
||||
return {
|
||||
'success': True,
|
||||
'count': len(suggestions),
|
||||
'suggestions': suggestions
|
||||
})
|
||||
}
|
||||
|
||||
def getRequestHeaders(self):
|
||||
return {
|
||||
|
||||
@@ -2,7 +2,7 @@ from couchpotato.core.event import addEvent
|
||||
from couchpotato.core.helpers.encoding import simplifyString, toUnicode
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.providers.movie.base import MovieProvider
|
||||
from libs.themoviedb import tmdb
|
||||
from themoviedb import tmdb
|
||||
import traceback
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
@@ -2,7 +2,6 @@ from __future__ import with_statement
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.event import addEvent, fireEvent
|
||||
from couchpotato.core.helpers.encoding import isInt, toUnicode
|
||||
from couchpotato.core.helpers.request import getParams, jsonified
|
||||
from couchpotato.core.helpers.variable import mergeDicts, tryInt
|
||||
from couchpotato.core.settings.model import Properties
|
||||
import ConfigParser
|
||||
@@ -170,18 +169,16 @@ class Settings(object):
|
||||
|
||||
|
||||
def view(self):
|
||||
return jsonified({
|
||||
return {
|
||||
'options': self.getOptions(),
|
||||
'values': self.getValues()
|
||||
})
|
||||
}
|
||||
|
||||
def saveView(self):
|
||||
def saveView(self, **kwargs):
|
||||
|
||||
params = getParams()
|
||||
|
||||
section = params.get('section')
|
||||
option = params.get('name')
|
||||
value = params.get('value')
|
||||
section = kwargs.get('section')
|
||||
option = kwargs.get('name')
|
||||
value = kwargs.get('value')
|
||||
|
||||
# See if a value handler is attached, use that as value
|
||||
new_value = fireEvent('setting.save.%s.%s' % (section, option), value, single = True)
|
||||
@@ -192,9 +189,9 @@ class Settings(object):
|
||||
# After save (for re-interval etc)
|
||||
fireEvent('setting.save.%s.%s.after' % (section, option), single = True)
|
||||
|
||||
return jsonified({
|
||||
return {
|
||||
'success': True,
|
||||
})
|
||||
}
|
||||
|
||||
def getProperty(self, identifier):
|
||||
from couchpotato import get_session
|
||||
|
||||
@@ -11,6 +11,7 @@ class Env(object):
|
||||
_appname = 'CouchPotato'
|
||||
|
||||
''' Environment variables '''
|
||||
_app = None
|
||||
_encoding = 'UTF-8'
|
||||
_debug = False
|
||||
_dev = False
|
||||
|
||||
+58
-40
@@ -1,13 +1,12 @@
|
||||
from argparse import ArgumentParser
|
||||
from couchpotato import web
|
||||
from couchpotato.api import api, NonBlockHandler
|
||||
from cache import FileSystemCache
|
||||
from couchpotato import KeyHandler
|
||||
from couchpotato.api import NonBlockHandler, ApiHandler
|
||||
from couchpotato.core.event import fireEventAsync, fireEvent
|
||||
from couchpotato.core.helpers.variable import getDataDir, tryInt
|
||||
from logging import handlers
|
||||
from tornado.httpserver import HTTPServer
|
||||
from tornado.web import Application, FallbackHandler
|
||||
from tornado.wsgi import WSGIContainer
|
||||
from werkzeug.contrib.cache import FileSystemCache
|
||||
from tornado.web import Application, StaticFileHandler, RedirectHandler
|
||||
import locale
|
||||
import logging
|
||||
import os.path
|
||||
@@ -172,11 +171,6 @@ def runCouchPotato(options, base_path, args, data_dir = None, log_dir = None, En
|
||||
db = Env.get('db_path')
|
||||
db_exists = os.path.isfile(db_path)
|
||||
|
||||
# Load configs & plugins
|
||||
loader = Env.get('loader')
|
||||
loader.preload(root = base_path)
|
||||
loader.run()
|
||||
|
||||
# Load migrations
|
||||
if db_exists:
|
||||
|
||||
@@ -201,17 +195,16 @@ def runCouchPotato(options, base_path, args, data_dir = None, log_dir = None, En
|
||||
from couchpotato.core.settings.model import setup
|
||||
setup()
|
||||
|
||||
# Fill database with needed stuff
|
||||
if not db_exists:
|
||||
fireEvent('app.initialize', in_order = True)
|
||||
|
||||
# Create app
|
||||
from couchpotato import app
|
||||
from couchpotato import WebHandler
|
||||
web_base = ('/' + Env.setting('url_base').lstrip('/') + '/') if Env.setting('url_base') else '/'
|
||||
Env.set('web_base', web_base)
|
||||
|
||||
api_key = Env.setting('api_key')
|
||||
url_base = '/' + Env.setting('url_base').lstrip('/') if Env.setting('url_base') else ''
|
||||
api_base = r'%sapi/%s/' % (web_base, api_key)
|
||||
Env.set('api_base', api_base)
|
||||
|
||||
# Basic config
|
||||
app.secret_key = api_key
|
||||
host = Env.setting('host', default = '0.0.0.0')
|
||||
# app.debug = development
|
||||
config = {
|
||||
@@ -222,36 +215,61 @@ def runCouchPotato(options, base_path, args, data_dir = None, log_dir = None, En
|
||||
'ssl_key': Env.setting('ssl_key', default = None),
|
||||
}
|
||||
|
||||
# Static path
|
||||
app.static_folder = os.path.join(base_path, 'couchpotato', 'static')
|
||||
web.add_url_rule('api/%s/static/<path:filename>' % api_key,
|
||||
endpoint = 'static',
|
||||
view_func = app.send_static_file)
|
||||
|
||||
# Register modules
|
||||
app.register_blueprint(web, url_prefix = '%s/' % url_base)
|
||||
app.register_blueprint(api, url_prefix = '%s/api/%s/' % (url_base, api_key))
|
||||
# Load the app
|
||||
application = Application([],
|
||||
log_function = lambda x : None,
|
||||
debug = config['use_reloader'],
|
||||
gzip = True,
|
||||
)
|
||||
Env.set('app', application)
|
||||
|
||||
|
||||
# Request handlers
|
||||
application.add_handlers(".*$", [
|
||||
(r'%snonblock/(.*)/' % api_base, NonBlockHandler),
|
||||
|
||||
# API handlers
|
||||
(r'%s(.*)/' % api_base, ApiHandler), # Main API handler
|
||||
(r'%sgetkey/' % web_base, KeyHandler), # Get API key
|
||||
(r'%s' % api_base, RedirectHandler, {"url": web_base + 'docs/'}), # API docs
|
||||
|
||||
# Catch all webhandlers
|
||||
(r'%s(.*)/' % web_base, WebHandler),
|
||||
(r'%s(.*)' % web_base, WebHandler),
|
||||
(r'(.*)', WebHandler),
|
||||
])
|
||||
|
||||
# Static paths
|
||||
static_path = '%sstatic/' % api_base
|
||||
for dir_name in ['fonts', 'images', 'scripts', 'style']:
|
||||
application.add_handlers(".*$", [
|
||||
('%s%s/(.*)' % (static_path, dir_name), StaticFileHandler, {'path': os.path.join(base_path, 'couchpotato', 'static', dir_name)})
|
||||
])
|
||||
Env.set('static_path', static_path);
|
||||
|
||||
|
||||
# Load configs & plugins
|
||||
loader = Env.get('loader')
|
||||
loader.preload(root = base_path)
|
||||
loader.run()
|
||||
|
||||
|
||||
# Fill database with needed stuff
|
||||
if not db_exists:
|
||||
fireEvent('app.initialize', in_order = True)
|
||||
|
||||
|
||||
# Go go go!
|
||||
from tornado.ioloop import IOLoop
|
||||
loop = IOLoop.current()
|
||||
|
||||
|
||||
# Some logging and fire load event
|
||||
try: log.info('Starting server on port %(port)s', config)
|
||||
except: pass
|
||||
fireEventAsync('app.load')
|
||||
|
||||
# Go go go!
|
||||
from tornado.ioloop import IOLoop
|
||||
web_container = WSGIContainer(app)
|
||||
web_container._log = _log
|
||||
loop = IOLoop.current()
|
||||
|
||||
|
||||
application = Application([
|
||||
(r'%s/api/%s/nonblock/(.*)/' % (url_base, api_key), NonBlockHandler),
|
||||
(r'.*', FallbackHandler, dict(fallback = web_container)),
|
||||
],
|
||||
log_function = lambda x : None,
|
||||
debug = config['use_reloader'],
|
||||
gzip = True,
|
||||
)
|
||||
|
||||
if config['ssl_cert'] and config['ssl_key']:
|
||||
server = HTTPServer(application, no_keep_alive = True, ssl_options = {
|
||||
|
||||
@@ -1,27 +1,28 @@
|
||||
{% autoescape None %}
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="{{ url_for('web.static', filename='style/api.css') }}" type="text/css">
|
||||
<link rel="stylesheet" href="{{ Env.get('static_path') }}style/api.css" type="text/css">
|
||||
<title>API documentation</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>CouchPotato API Documentation</h1>
|
||||
<div class="api">
|
||||
You can access the API via <pre>{{ fireEvent('app.api_url', single = True)|safe }}/</pre>
|
||||
You can access the API via <pre>{{ Env.get('api_base') }}</pre>
|
||||
To see it in action, have a look at the webinterface with Firebug (on firefox) or the development tools included in Chrome.
|
||||
All the data that you see there are from the API.
|
||||
<br />
|
||||
<br />
|
||||
A normal API call:
|
||||
<pre><a href="{{ fireEvent('app.api_url', single = True)|safe }}/updater.info/">{{ fireEvent('app.api_url', single = True)|safe }}/updater.info/</a></pre>
|
||||
<pre><a href="{{ Env.get('api_base') }}updater.info/">{{ Env.get('api_base') }}updater.info/</a></pre>
|
||||
<br />
|
||||
You can also use the API over another domain using JSONP, the callback function should be in 'callback_func'
|
||||
<pre><a href="{{ fireEvent('app.api_url', single = True)|safe }}/updater.info/?callback_func=myfunction">{{ fireEvent('app.api_url', single = True)|safe }}/updater.info/?callback_func=myfunction</a></pre>
|
||||
<pre><a href="{{ Env.get('api_base') }}updater.info/?callback_func=myfunction">{{ Env.get('api_base') }}updater.info/?callback_func=myfunction</a></pre>
|
||||
<br />
|
||||
<br />
|
||||
Get the API key:
|
||||
<pre><a href="{{ url_for('web.index') }}getkey/?p=md5(password)&u=md5(username)">{{ url_for('web.index') }}getkey/?p=md5(password)&u=md5(username)</a></pre>
|
||||
<pre><a href="{{ Env.get('web_base') }}getkey/?p=md5(password)&u=md5(username)">{{ Env.get('web_base') }}getkey/?p=md5(password)&u=md5(username)</a></pre>
|
||||
Will return {"api_key": "XXXXXXXXXX", "success": true}. When username or password is empty you don't need to md5 it.
|
||||
<br />
|
||||
</div>
|
||||
@@ -41,9 +42,9 @@
|
||||
<td class="type">{{ api_docs[route]['params'][param].get('type', 'string') }}</td>
|
||||
<td class="description">{{ api_docs[route]['params'][param]['desc'] }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% end %}
|
||||
</table>
|
||||
{% endif %}
|
||||
{% end %}
|
||||
|
||||
{% if api_docs[route].get('return') %}
|
||||
<h3>Return</h3>
|
||||
@@ -52,14 +53,14 @@
|
||||
{% if api_docs[route]['return'].get('example') %}
|
||||
<div class="example">
|
||||
<h4>Example</h4>
|
||||
<pre>{{ api_docs[route]['return'].get('example', '')|safe }}</pre>
|
||||
<pre>{{ api_docs[route]['return'].get('example', '') }}</pre>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% end %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% end %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% end %}
|
||||
{% end %}
|
||||
|
||||
<div class="missing">
|
||||
<h1>Missing documentation</h1>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
{% autoescape None %}
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
@@ -5,38 +6,38 @@
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
|
||||
{% for url in fireEvent('clientscript.get_styles', as_html = True, location = 'front', single = True) %}
|
||||
<link rel="stylesheet" href="{{ url_for('web.index') }}{{ url }}" type="text/css">{% endfor %}
|
||||
<link rel="stylesheet" href="{{ Env.get('web_base') }}{{ url }}" type="text/css">{% end %}
|
||||
{% for url in fireEvent('clientscript.get_scripts', as_html = True, location = 'front', single = True) %}
|
||||
<script type="text/javascript" src="{{ url_for('web.index') }}{{ url }}"></script>{% endfor %}
|
||||
<script type="text/javascript" src="{{ Env.get('web_base') }}{{ url }}"></script>{% end %}
|
||||
|
||||
{% for url in fireEvent('clientscript.get_scripts', as_html = True, location = 'head', single = True) %}
|
||||
<script type="text/javascript" src="{{ url_for('web.index') }}{{ url }}"></script>{% endfor %}
|
||||
<script type="text/javascript" src="{{ Env.get('web_base') }}{{ url }}"></script>{% end %}
|
||||
{% for url in fireEvent('clientscript.get_styles', as_html = True, location = 'head', single = True) %}
|
||||
<link rel="stylesheet" href="{{ url_for('web.index') }}{{ url }}" type="text/css">{% endfor %}
|
||||
<link rel="stylesheet" href="{{ Env.get('web_base') }}{{ url }}" type="text/css">{% end %}
|
||||
|
||||
<link href="{{ url_for('web.static', filename='images/favicon.ico') }}" rel="icon" type="image/x-icon" />
|
||||
<link rel="apple-touch-icon" href="{{ url_for('web.static', filename='images/homescreen.png') }}" />
|
||||
<link href="{{ Env.get('static_path') }}images/favicon.ico" rel="icon" type="image/x-icon" />
|
||||
<link rel="apple-touch-icon" href="{{ Env.get('static_path') }}images/homescreen.png" />
|
||||
|
||||
<script type="text/javascript" src="https://www.youtube.com/player_api" defer="defer"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
|
||||
if($(window).getSize().x <= 480)
|
||||
window.addEvent('load', function() {
|
||||
|
||||
|
||||
setTimeout(function(){
|
||||
window.scrollTo(0, 1);
|
||||
window.scrollTo(0, 0);
|
||||
}, 100);
|
||||
|
||||
|
||||
});
|
||||
|
||||
window.addEvent('domready', function() {
|
||||
new Uniform();
|
||||
|
||||
Api.setup({
|
||||
'url': {{ url_for('api.index')|tojson|safe }},
|
||||
'path_sep': {{ sep|tojson|safe }},
|
||||
'url': {{ json_encode(Env.get('api_base')) }},
|
||||
'path_sep': {{ json_encode(sep) }},
|
||||
'is_remote': false
|
||||
});
|
||||
|
||||
@@ -61,29 +62,29 @@
|
||||
}
|
||||
|
||||
Quality.setup({
|
||||
'profiles': {{ fireEvent('profile.all', single = True)|tojson|safe }},
|
||||
'qualities': {{ fireEvent('quality.all', single = True)|tojson|safe }}
|
||||
'profiles': {{ json_encode(fireEvent('profile.all', single = True)) }},
|
||||
'qualities': {{ json_encode(fireEvent('quality.all', single = True)) }}
|
||||
});
|
||||
|
||||
Status.setup({{ fireEvent('status.all', single = True)|tojson|safe }});
|
||||
Status.setup({{ json_encode(fireEvent('status.all', single = True)) }});
|
||||
|
||||
File.Type.setup({{ fireEvent('file.types', single = True)|tojson|safe }});
|
||||
File.Type.setup({{ json_encode(fireEvent('file.types', single = True)) }});
|
||||
|
||||
App.setup({
|
||||
'base_url': {{ url_for('web.index')|tojson|safe }},
|
||||
'args': {{ env.get('args')|tojson|safe }},
|
||||
'options': {{ ('%s' % env.get('options'))|tojson|safe }},
|
||||
'app_dir': {{ env.get('app_dir')|tojson|safe }},
|
||||
'data_dir': {{ env.get('data_dir')|tojson|safe }},
|
||||
'pid': {{ env.getPid()|tojson|safe }},
|
||||
'userscript_version': {{ fireEvent('userscript.get_version', single = True)|tojson|safe }}
|
||||
'base_url': {{ json_encode(Env.get('web_base')) }},
|
||||
'args': {{ json_encode(Env.get('args')) }},
|
||||
'options': {{ json_encode(('%s' % Env.get('options'))) }},
|
||||
'app_dir': {{ json_encode(Env.get('app_dir')) }},
|
||||
'data_dir': {{ json_encode(Env.get('data_dir')) }},
|
||||
'pid': {{ json_encode(Env.getPid()) }},
|
||||
'userscript_version': {{ json_encode(fireEvent('userscript.get_version', single = True)) }}
|
||||
});
|
||||
})
|
||||
|
||||
{% if env.setting('show_wizard') %}
|
||||
{% if Env.setting('show_wizard') %}
|
||||
if(!window.location.href.contains('wizard'))
|
||||
window.location = '{{ url_for('web.index') }}wizard/'
|
||||
{% endif %}
|
||||
window.location = '{{ Env.get('web_base') }}wizard/'
|
||||
{% end %}
|
||||
|
||||
</script>
|
||||
<title>CouchPotato</title>
|
||||
|
||||
Reference in new issue
Block a user