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 main = {}
import configparser database = {}
import os
import inspect
def init(): def init(config=None):
global config global main
global database
main = config["MAIN"]
database = config["DATABASE"]
# Decide what config-file to use return True
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()
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(): def contentpath():
return(config.get('Database','contentpath')) return database["contentpath"]
def nfosuffix(): 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 zipfile
import pathlib import pathlib
global gamelist
gamelist = [] gamelist = []

View File

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

View File

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

View File

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

View File

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

View File

@ -4,7 +4,7 @@ from flask import render_template, redirect, url_for, request, Blueprint
import bcnsGDSSite.modules.init import bcnsGDSSite.modules.init
import base64 import base64
host_endpoint = bcnsGDSSite.modules.init.get_apihost() apihost = bcnsGDSSite.modules.init.apihost()
languages = bcnsGDSSite.modules.init.get_languages() languages = bcnsGDSSite.modules.init.get_languages()
app_language = bcnsGDSSite.modules.init.app_language app_language = bcnsGDSSite.modules.init.app_language
@ -26,7 +26,7 @@ def showgamelist(lang_code):
glist = None glist = None
try: try:
glist = json.loads((requests.get( glist = json.loads((requests.get(
host_endpoint + '/gamelist').content).decode()) apihost + '/gamelist').content).decode())
# Sorting list alphabetically # Sorting list alphabetically
glist = sorted(glist, key=lambda d: d['game']['title']) glist = sorted(glist, key=lambda d: d['game']['title'])
@ -48,7 +48,7 @@ def showgamelist(lang_code):
if thumbnails == "get": if thumbnails == "get":
try: try:
thumbnailslist = json.loads((requests.get( thumbnailslist = json.loads((requests.get(
host_endpoint + '/gamelist/displayimage').content).decode()) apihost + '/gamelist/displayimage').content).decode())
except request.exceptions.ConnectionError as e: except request.exceptions.ConnectionError as e:
print(e) print(e)
@ -70,6 +70,6 @@ def showgamelist(lang_code):
def gamelist_update(lang_code): def gamelist_update(lang_code):
lang_code = lang(lang_code) lang_code = lang(lang_code)
response = requests.get( response = requests.get(
host_endpoint + '/gamelist/update') apihost + '/gamelist/update')
if response.status_code == 200: 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 = {} languages = {}
global apihost
apihost = "http://media.odecif.net:5501"
global app_language
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): def set_languages(languages_dict):
global languages global languages
@ -36,7 +45,3 @@ def set_applanguage(new_applanguage):
global app_language global app_language
app_language = new_applanguage app_language = new_applanguage
return app_language return app_language
def init():
global apihost
apihost = ""