Api doc base
This commit is contained in:
+15
-6
@@ -1,21 +1,30 @@
|
||||
from couchpotato.core.helpers.request import jsonified
|
||||
from flask.blueprints import Blueprint
|
||||
from flask.templating import render_template
|
||||
|
||||
api = Blueprint('api', __name__)
|
||||
api_docs = {}
|
||||
api_docs_missing = []
|
||||
|
||||
def addApiView(route, func, static = False):
|
||||
api.add_url_rule(route + ('' if static else '/'), endpoint = route.replace('.', '-') if route else 'index', view_func = func)
|
||||
def addApiView(route, func, static = False, docs = None):
|
||||
api.add_url_rule(route + ('' if static else '/'), endpoint = route.replace('.', '::') if route else 'index', view_func = func)
|
||||
if docs:
|
||||
api_docs[route[4:] if route[0:4] == 'api.' else route] = docs
|
||||
else:
|
||||
api_docs_missing.append(route)
|
||||
|
||||
""" Api view """
|
||||
def index():
|
||||
from couchpotato import app
|
||||
|
||||
from couchpotato import app
|
||||
routes = []
|
||||
for route, x in sorted(app.view_functions.iteritems()):
|
||||
if route[0:4] == 'api.':
|
||||
routes += [route[4:]]
|
||||
routes += [route[4:].replace('::', '.')]
|
||||
|
||||
return jsonified({'routes': routes})
|
||||
if api_docs.get(''):
|
||||
del api_docs['']
|
||||
del api_docs_missing['']
|
||||
return render_template('api.html', routes = sorted(routes), api_docs = api_docs, api_docs_missing = sorted(api_docs_missing))
|
||||
|
||||
addApiView('', index)
|
||||
addApiView('default', index)
|
||||
|
||||
@@ -24,9 +24,17 @@ class Core(Plugin):
|
||||
shutdown_started = False
|
||||
|
||||
def __init__(self):
|
||||
addApiView('app.shutdown', self.shutdown)
|
||||
addApiView('app.restart', self.restart)
|
||||
addApiView('app.available', self.available)
|
||||
addApiView('app.shutdown', self.shutdown, docs = {
|
||||
'desc': 'Shutdown the app.',
|
||||
'return': {'type': 'string: shutdown'}
|
||||
})
|
||||
addApiView('app.restart', self.restart, docs = {
|
||||
'desc': 'Restart the app.',
|
||||
'return': {'type': 'string: restart'}
|
||||
})
|
||||
addApiView('app.available', self.available, docs = {
|
||||
'desc': 'Check if app available.'
|
||||
})
|
||||
|
||||
addEvent('app.crappy_shutdown', self.crappyShutdown)
|
||||
addEvent('app.crappy_restart', self.crappyRestart)
|
||||
|
||||
@@ -29,9 +29,25 @@ class Updater(Plugin):
|
||||
|
||||
addEvent('app.load', self.check)
|
||||
|
||||
addApiView('updater.info', self.getInfo)
|
||||
addApiView('updater.info', self.getInfo, docs = {
|
||||
'desc': 'Get updater information',
|
||||
'return': {
|
||||
'type': 'object',
|
||||
'example': """
|
||||
{
|
||||
'repo_name': "Name of used repository",
|
||||
'last_check': "last checked for update",
|
||||
'update_version': "available update version or empty",
|
||||
'version': current_cp_version
|
||||
}
|
||||
"""
|
||||
}
|
||||
})
|
||||
addApiView('updater.update', self.doUpdateView)
|
||||
addApiView('updater.check', self.checkView)
|
||||
addApiView('updater.check', self.checkView, docs = {
|
||||
'desc': 'Check for available update',
|
||||
'return': {'type': 'see updater.info'}
|
||||
})
|
||||
|
||||
def getInfo(self):
|
||||
|
||||
|
||||
@@ -11,7 +11,18 @@ if os.name == 'nt':
|
||||
class FileBrowser(Plugin):
|
||||
|
||||
def __init__(self):
|
||||
addApiView('directory.list', self.view)
|
||||
addApiView('directory.list', self.view, docs = {
|
||||
'desc': 'Return the directory list of a given directory',
|
||||
'params': {
|
||||
'path': {'desc': 'The directory to scan'},
|
||||
'show_hidden': {'desc': 'Also show hidden files'}
|
||||
},
|
||||
'return': {'type': 'object', 'example': """{
|
||||
'is_root': bool, //is top most folder
|
||||
'empty': bool, //directory is empty
|
||||
'dirs': array, //directory names
|
||||
}"""}
|
||||
})
|
||||
|
||||
def getDirectories(self, path = '/', show_hidden = True):
|
||||
|
||||
|
||||
@@ -19,7 +19,13 @@ class FileManager(Plugin):
|
||||
addEvent('file.download', self.download)
|
||||
addEvent('file.types', self.getTypes)
|
||||
|
||||
addApiView('file.cache/<path:filename>', self.showCacheFile, static = True)
|
||||
addApiView('file.cache/<path:filename>', self.showCacheFile, static = True, docs = {
|
||||
'desc': 'Return a file from the cp_data/cache directory',
|
||||
'params': {
|
||||
'filename': {'desc': 'path/filename of the wanted file'}
|
||||
},
|
||||
'return': {'type': 'file'}
|
||||
})
|
||||
|
||||
def showCacheFile(self, filename = ''):
|
||||
|
||||
|
||||
@@ -12,9 +12,27 @@ log = CPLog(__name__)
|
||||
class Logging(Plugin):
|
||||
|
||||
def __init__(self):
|
||||
addApiView('logging.get', self.get)
|
||||
addApiView('logging.clear', self.clear)
|
||||
addApiView('logging.log', self.log)
|
||||
addApiView('logging.get', self.get, docs = {
|
||||
'desc': 'Get the full log file by number',
|
||||
'params': {
|
||||
'nr': {'desc': 'Number of the log to get.'}
|
||||
},
|
||||
'return': {'type': 'object', 'example': """{
|
||||
'success': True,
|
||||
'log': string, //Log file
|
||||
'total': int, //Total log files available
|
||||
}"""}
|
||||
})
|
||||
addApiView('logging.clear', self.clear, docs = {
|
||||
'desc': 'Remove all the log files'
|
||||
})
|
||||
addApiView('logging.log', self.log, docs = {
|
||||
'desc': 'Get the full log file by number',
|
||||
'params': {
|
||||
'type': {'desc': 'Type of logging, default "error"'},
|
||||
'**kwargs': {'type':'object', 'desc': 'All other params will be printed in the log string.'},
|
||||
}
|
||||
})
|
||||
|
||||
def get(self):
|
||||
|
||||
|
||||
@@ -21,7 +21,12 @@ class Manage(Plugin):
|
||||
fireEvent('scheduler.interval', identifier = 'manage.update_library', handle = self.updateLibrary, hours = 2)
|
||||
|
||||
addEvent('manage.update', self.updateLibrary)
|
||||
addApiView('manage.update', self.updateLibraryView)
|
||||
addApiView('manage.update', self.updateLibraryView, docs = {
|
||||
'desc': 'Update the library by scanning for new movies',
|
||||
'params': {
|
||||
'full': {'desc': 'Do a full update or just recently changed/added movies.'},
|
||||
}
|
||||
})
|
||||
|
||||
if not Env.get('dev'):
|
||||
addEvent('app.load', self.updateLibrary)
|
||||
|
||||
@@ -26,9 +26,37 @@ class MoviePlugin(Plugin):
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
addApiView('movie.search', self.search)
|
||||
addApiView('movie.list', self.listView)
|
||||
addApiView('movie.refresh', self.refresh)
|
||||
addApiView('movie.search', self.search, docs = {
|
||||
'desc': 'Search the movie providers for a movie',
|
||||
'params': {
|
||||
'q': {'desc': 'The (partial) movie name you want to search for'},
|
||||
},
|
||||
'return': {'type': 'object', 'example': """{
|
||||
'success': True,
|
||||
'empty': bool, any movies returned or not,
|
||||
'movies': array, movies found,
|
||||
}"""}
|
||||
})
|
||||
addApiView('movie.list', self.listView, docs = {
|
||||
'desc': 'List movies in wanted list',
|
||||
'params': {
|
||||
'status': {'type': 'array or csv', 'desc': 'Filter movie by status. Example:"active,done"'},
|
||||
'limit_offset': {'desc': 'Limit the movie list. Examples: "50", "50,30"'},
|
||||
'starts_with': {'desc': 'Starts with these characters. Example: "a" returns all movies starting with the letter "a"'},
|
||||
'search': {'desc': 'Search movie title'},
|
||||
},
|
||||
'return': {'type': 'object', 'example': """{
|
||||
'success': True,
|
||||
'empty': bool, any movies returned or not,
|
||||
'movies': array, movies found,
|
||||
}"""}
|
||||
})
|
||||
addApiView('movie.refresh', self.refresh, docs = {
|
||||
'desc': 'Refresh a movie by id',
|
||||
'params': {
|
||||
'id': {'desc': 'The id of the movie that needs to be refreshed'},
|
||||
}
|
||||
})
|
||||
addApiView('movie.available_chars', self.charView)
|
||||
|
||||
addApiView('movie.add', self.addView)
|
||||
|
||||
@@ -16,8 +16,41 @@ class Settings(object):
|
||||
|
||||
def __init__(self):
|
||||
|
||||
addApiView('settings', self.view)
|
||||
addApiView('settings.save', self.saveView)
|
||||
addApiView('settings', self.view, docs = {
|
||||
'desc': 'Return the options and its values of settings.conf. Including the default values and group ordering used on the settings page.',
|
||||
'return': {'type': 'object', 'example': """{
|
||||
// objects like in __init__.py of plugin
|
||||
"options": {
|
||||
"moovee" : {
|
||||
"groups" : [{
|
||||
"description" : "SD movies only",
|
||||
"name" : "#alt.binaries.moovee",
|
||||
"options" : [{
|
||||
"default" : false,
|
||||
"name" : "enabled",
|
||||
"type" : "enabler"
|
||||
}],
|
||||
"tab" : "providers"
|
||||
}],
|
||||
"name" : "moovee"
|
||||
}
|
||||
},
|
||||
// object structured like settings.conf
|
||||
"values": {
|
||||
"moovee": {
|
||||
"enabled": false
|
||||
}
|
||||
}
|
||||
}"""}
|
||||
})
|
||||
addApiView('settings.save', self.saveView, docs = {
|
||||
'desc': 'Save setting to config file (settings.conf)',
|
||||
'params': {
|
||||
'section': {'desc': 'The section name in settings.conf'},
|
||||
'option': {'desc': 'The option name'},
|
||||
'value': {'desc': 'The value you want to save'},
|
||||
}
|
||||
})
|
||||
|
||||
def setFile(self, config_file):
|
||||
self.file = config_file
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
html {
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, Geneva, sans-serif;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5 {
|
||||
clear: both;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 25px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
pre {
|
||||
background: #ccc;
|
||||
font-family: monospace;
|
||||
margin: 0;
|
||||
padding: 2%;
|
||||
width: 96%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.api {
|
||||
margin-bottom: 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.api .description {
|
||||
color: #333;
|
||||
padding: 0 0 5px;
|
||||
}
|
||||
|
||||
.api .params {
|
||||
background: #f5f5f5;
|
||||
width: 100%;
|
||||
}
|
||||
.api h3 {
|
||||
clear: both;
|
||||
float: left;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.api .params {
|
||||
float: left;
|
||||
width: 700px;
|
||||
}
|
||||
|
||||
.api .params .param {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.api .params .param th {
|
||||
text-align: left;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.api .param .type {
|
||||
font-style: italic;
|
||||
margin-right: 10px;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.api .return {
|
||||
float: left;
|
||||
width: 700px;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="{{ url_for('web.static', filename='style/api.css') }}" type="text/css">
|
||||
<title>API documentation</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>API</h1>
|
||||
{% for route in routes %}
|
||||
{% if api_docs.get(route) %}
|
||||
<div class="api">
|
||||
<h2>{{route}}</h2>
|
||||
<div class="description">{{api_docs[route].get('desc', '')}}</div>
|
||||
|
||||
{% if api_docs[route].get('params') %}
|
||||
<h3>Params</h3>
|
||||
<table class="params">
|
||||
{% for param in api_docs[route]['params'] %}
|
||||
<tr class="param">
|
||||
<th>{{param}}</th>
|
||||
<td class="type">{{ api_docs[route]['params'][param].get('type', 'string') }}</td>
|
||||
<td class="description">{{ api_docs[route]['params'][param]['desc'] }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% endif %}
|
||||
|
||||
{% if api_docs[route].get('return') %}
|
||||
<h3>Return</h3>
|
||||
<div class="return">
|
||||
<div class="type">{{ api_docs[route]['return'].get('type', '{"success": True}') }}</div>
|
||||
{% if api_docs[route]['return'].get('example') %}
|
||||
<div class="example">
|
||||
<h4>Example</h4>
|
||||
<pre>{{ api_docs[route]['return'].get('example', '')|safe }}</pre>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
<h1>Missing documentation</h1>
|
||||
<div class="missing">{{', '.join(api_docs_missing)}}</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user