Prepare API for gunicorn

It is now possible to run the API with gunicorn using
gunicorn -w 4 -b 0.0.0.0:8001 'bcnsGDSAPI:create_app()'.

Also did other small changes.
This commit is contained in:
odecif 2023-11-21 12:51:35 +01:00
parent 631dc761c6
commit 77b0bcb94a
10 changed files with 123 additions and 91 deletions

52
bcnsGDSAPI/__init__.py Normal file
View File

@ -0,0 +1,52 @@
from flask import request, jsonify, Flask
import json
from datetime import date
import tomllib
import sys
import os
class MyJSONEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, date):
return o.isoformat()
return super().default(o)
def create_app(test_config=None):
app = Flask(__name__, instance_relative_config=True)
app.config['JSON_AS_ASCII'] = False
if test_config is None:
app.config.from_file('bcnsgdsapi-config.toml',
load=tomllib.load, text=False)
else:
app.config.from_mapping(test_config)
try:
os.makedirs(app.instance_path)
except OSError:
pass
import bcnsGDSAPI.modules.db_connect
bcnsGDSAPI.modules.db_connect.init(app.config)
from bcnsGDSAPI.modules.gamelist import gamelist
from bcnsGDSAPI.modules.game import game
app.register_blueprint(gamelist)
app.register_blueprint(game)
# Just a simple homepage, nothing fancy
@app.route('/', methods=['GET'])
def home():
if 'id' not in request.args:
return "<h1>BCNS Game Distribution System</h1><p>API-endpoint</p>"
else:
return jsonify("I see you're a man of culture as well")
return app
if __name__ == "__main__":
app = create_app()
app.run(host="0.0.0.0", port=8001, debug=True)

View File

@ -1,37 +1,19 @@
import argparse
import configparser
import os
import inspect
main = {}
database = {}
def init():
global config
def init(config=None):
global main
global database
main = config["MAIN"]
database = config["DATABASE"]
# Decide what config-file to use
parser = argparse.ArgumentParser()
parser.add_argument('-e', '--environment', choices=['dev', 'prod', 'test'],
default='prod',
help='choose what environment type to run.'
' Defaults to prod')
args = parser.parse_args()
return True
if args.environment == 'dev':
print("Using devapi.conf")
configfile = '/../devapi.conf'
elif args.environment == 'test':
print("Using testapi.conf")
configfile = '/../testapi.conf'
else:
print("Using api.conf")
configfile = '/../api.conf'
config = configparser.RawConfigParser()
config.read(os.path.dirname(
os.path.abspath(inspect.getfile(
inspect.currentframe()))) + configfile)
def contentpath():
return(config.get('Database','contentpath'))
return database["contentpath"]
def nfosuffix():
return(config.get('Database','nfo_suffix'))
return database["nfo_suffix"]

View File

@ -4,7 +4,6 @@ from io import BytesIO
import zipfile
import pathlib
global gamelist
gamelist = []

View File

@ -1,17 +1,17 @@
from __main__ import app
from flask import jsonify, request, send_from_directory, send_file
from flask import jsonify, request, send_from_directory, send_file, Blueprint
import base64
import json
import modules.db_connect
from modules.functions import reduceartcv2, make_archive
import bcnsGDSAPI.modules.db_connect
from bcnsGDSAPI.modules.functions import reduceartcv2, make_archive
import os
contentpath = modules.db_connect.contentpath()
contentpath = bcnsGDSAPI.modules.db_connect.contentpath()
game = Blueprint('game', __name__, template_folder='templates')
# Fetch and present all information from one _index.nfo-file
@app.route('/game', methods=['POST'])
def game(
@game.route('/game', methods=['POST'])
def showgame(
predefinednfo=False,
return_dict=False,
skip_artwork=False,
@ -59,8 +59,8 @@ def game(
# Fetch all artwork from a given _index.nfo-file
@app.route('/game/artwork/size/<size>', methods=['POST'])
@app.route('/game/artwork', methods=['POST'])
@game.route('/game/artwork/size/<size>', methods=['POST'])
@game.route('/game/artwork', methods=['POST'])
def artwork(size='max'):
f = open(request.json['nfo'], 'r')
nfo = json.load(f)
@ -89,7 +89,7 @@ def artwork(size='max'):
# Serve a file. Takes the following object parameters:
# nfopath base64-encoded full path to specific game nfo-file
# filepath base64-encoded path to file starting from game dir
@app.route('/getfile', methods=["GET"])
@game.route('/getfile', methods=["GET"])
def getfile():
nfopath = os.path.dirname(
base64.b64decode(request.json['nfopath']).decode())
@ -98,7 +98,7 @@ def getfile():
# Game folder as ZIP-file (kinda slow)
@app.route('/getzipfile', methods=["GET"])
@game.route('/getzipfile', methods=["GET"])
def getzipfile():
nfopath = base64.b64decode(request.json).decode()
nfo = json.load(open(nfopath, 'r'))

View File

@ -1,20 +1,20 @@
from __main__ import app
from flask import jsonify, make_response
import modules.db_connect
from modules.functions import get_gamelist, set_gamelist
import modules.game
from modules.gamelist_functions import (
from flask import jsonify, make_response, Blueprint
import bcnsGDSAPI.modules.db_connect
from bcnsGDSAPI.modules.functions import get_gamelist, set_gamelist
import bcnsGDSAPI.modules.game
from bcnsGDSAPI.modules.gamelist_functions import (
get_threaded_thumbnails,
update_threaded_thumbnails)
import glob
contentpath = modules.db_connect.contentpath()
nfosuffix = modules.db_connect.nfosuffix()
contentpath = bcnsGDSAPI.modules.db_connect.contentpath()
nfosuffix = bcnsGDSAPI.modules.db_connect.nfosuffix()
gamelist = Blueprint('gamelist', __name__, template_folder='templates')
# Collects all _index.nfo-files present and crunches them into a list of
# games.
@app.route('/gamelist', methods=['GET'])
@gamelist.route('/gamelist', methods=['GET'])
def show_gamelist():
if get_gamelist():
return jsonify(get_gamelist())
@ -23,7 +23,7 @@ def show_gamelist():
# Updates the gamelist by searching for new nfo's
@app.route('/gamelist/update', methods=['GET'])
@gamelist.route('/gamelist/update', methods=['GET'])
def update_gamelist():
nfolist = list(dict.fromkeys(glob.glob(
str(contentpath)+'/**/**/*'+nfosuffix, recursive=True)))
@ -31,7 +31,7 @@ def update_gamelist():
glist = []
for nfo in nfolist:
try:
game = modules.game.game(nfo, True, True, True)
game = bcnsGDSAPI.modules.game.showgame(nfo, True, True, True)
glist.append(game)
except Exception as e:
print(nfo, e)
@ -41,7 +41,7 @@ def update_gamelist():
# Fetch displayimage for all nfo-files
@app.route('/gamelist/displayimage')
@gamelist.route('/gamelist/displayimage')
def get_displayimages(update=False):
thumbnails = get_threaded_thumbnails()
if (len(thumbnails) == 0) or update:
@ -50,7 +50,7 @@ def get_displayimages(update=False):
# Update displayimages
@app.route('/gamelist/displayimage/update')
@gamelist.route('/gamelist/displayimage/update')
def update_displayimages():
update_threaded_thumbnails()
return make_response("<h1>Success</h1>", 200)

View File

@ -1,14 +1,14 @@
import modules.db_connect
import bcnsGDSAPI.modules.db_connect
import glob
import os
import json
from modules.functions import reduceartcv2
from bcnsGDSAPI.modules.functions import reduceartcv2
import threading
from queue import Queue
import time
contentpath = modules.db_connect.contentpath()
nfosuffix = modules.db_connect.nfosuffix()
contentpath = bcnsGDSAPI.modules.db_connect.contentpath()
nfosuffix = bcnsGDSAPI.modules.db_connect.nfosuffix()
global nfofiles
nfofiles = []

View File

@ -1,8 +1,6 @@
from datetime import date
from flask import render_template, Flask, request
from json import JSONEncoder
from bcnsGDSSite.modules.gamelist import gamelist
from bcnsGDSSite.modules.game import game
import bcnsGDSSite.modules.init
import os
import inspect
@ -21,9 +19,6 @@ class MyJSONEncoder(JSONEncoder):
def create_app(test_config=None):
app = Flask(__name__, instance_relative_config=True)
app.register_blueprint(gamelist)
app.register_blueprint(game)
print(app.url_map)
app.config.from_mapping(
SECRET_KEY='dev',
DATABASE=os.path.join(app.instance_path, 'bcns_gds.sqlite'),
@ -44,14 +39,13 @@ def create_app(test_config=None):
except OSError:
pass
bcnsGDSSite.modules.init.init(app.config)
# import bcnsGDSSite.modules.gamelist # noqa: E402
bcnsGDSSite.modules.init.set_apihost("http://" +
app.config['API']['host'] +
':' + app.config['API']['port'])
from bcnsGDSSite.modules.gamelist import gamelist
from bcnsGDSSite.modules.game import game
app.register_blueprint(gamelist)
app.register_blueprint(game)
bcnsGDSSite.modules.init.set_applanguage(
app.config['MAIN']['app_language'])
languages = bcnsGDSSite.modules.init.get_languages()
if not languages:
languages_path = os.path.dirname(

View File

@ -4,7 +4,7 @@ import base64
from flask import render_template, request, Response, Blueprint
import bcnsGDSSite.modules.init
host_endpoint = bcnsGDSSite.modules.init.get_apihost()
apihost = bcnsGDSSite.modules.init.apihost()
languages = bcnsGDSSite.modules.init.get_languages()
app_language = bcnsGDSSite.modules.init.app_language
@ -27,7 +27,7 @@ def showgame(lang_code):
gamepath = request.args.get("gamepath")
game = json.loads((requests.post(
host_endpoint + '/game', json=gamepath).content).decode())
apihost + '/game', json=gamepath).content).decode())
game['game']['plot'] = game['game']['plot'].split('\\n')
if 'linuxinstructions' in game['game']:
@ -54,7 +54,7 @@ def download(lang_code):
gametitle = request.args.get("gametitle")
targettype = request.args.get("targettype")
if "zip" in targettype:
gamezip = requests.get(host_endpoint + '/getzipfile', json=gamepath)
gamezip = requests.get(apihost + '/getzipfile', json=gamepath)
return Response(gamezip, mimetype="application/zip",
headers={
"Content-Disposition":
@ -77,7 +77,7 @@ def getfile(lang_code):
filepath_enc = base64.b64encode(filepath.encode('utf-8')).decode()
jsonbody = {'nfopath': gamepath, 'filepath': filepath_enc}
print(gamepath, filepath)
file = requests.get(host_endpoint + '/getfile', json=jsonbody)
file = requests.get(apihost + '/getfile', json=jsonbody)
return Response(file, mimetype="application/pdf",
headers={
"Content-Disposition":

View File

@ -4,7 +4,7 @@ from flask import render_template, redirect, url_for, request, Blueprint
import bcnsGDSSite.modules.init
import base64
host_endpoint = bcnsGDSSite.modules.init.get_apihost()
apihost = bcnsGDSSite.modules.init.apihost()
languages = bcnsGDSSite.modules.init.get_languages()
app_language = bcnsGDSSite.modules.init.app_language
@ -26,7 +26,7 @@ def showgamelist(lang_code):
glist = None
try:
glist = json.loads((requests.get(
host_endpoint + '/gamelist').content).decode())
apihost + '/gamelist').content).decode())
# Sorting list alphabetically
glist = sorted(glist, key=lambda d: d['game']['title'])
@ -48,7 +48,7 @@ def showgamelist(lang_code):
if thumbnails == "get":
try:
thumbnailslist = json.loads((requests.get(
host_endpoint + '/gamelist/displayimage').content).decode())
apihost + '/gamelist/displayimage').content).decode())
except request.exceptions.ConnectionError as e:
print(e)
@ -70,6 +70,6 @@ def showgamelist(lang_code):
def gamelist_update(lang_code):
lang_code = lang(lang_code)
response = requests.get(
host_endpoint + '/gamelist/update')
apihost + '/gamelist/update')
if response.status_code == 200:
return redirect(url_for('gamelist', lang_code=lang_code))
return redirect(url_for('gamelist.showgamelist', lang_code=lang_code))

View File

@ -1,14 +1,23 @@
import os
import inspect
global languages
languages = {}
global apihost
apihost = "http://media.odecif.net:5501"
global app_language
app_language = ""
main = {}
api = {}
def init(config=None):
global main
global api
main = config["MAIN"]
api = config["API"]
def apihost():
return (
api["protocol"] + "://" +
api["host"] + ":" +
api["port"]
)
def set_languages(languages_dict):
global languages
@ -36,7 +45,3 @@ def set_applanguage(new_applanguage):
global app_language
app_language = new_applanguage
return app_language
def init():
global apihost
apihost = ""