From 0f742120638485fffa554d2d30ef0d360613f2b8 Mon Sep 17 00:00:00 2001 From: odecif Date: Wed, 26 Oct 2022 09:17:21 +0200 Subject: [PATCH] Uploaded to gitea --- .gitignore | 4 ++ api/api.conf | 11 ++++ api/api.py | 48 ++++++++++++++ api/modules/db_connect.py | 34 ++++++++++ api/modules/functions.py | 20 ++++++ api/modules/game.py | 65 ++++++++++++++++++ api/modules/gamelist.py | 24 +++++++ site/index.py | 76 +++++++++++++++++++++ site/language/en_US.json | 35 ++++++++++ site/language/sv_SE.json | 7 ++ site/modules/game.py | 44 +++++++++++++ site/modules/gamelist.py | 29 +++++++++ site/modules/init.py | 54 +++++++++++++++ site/site.conf | 8 +++ site/static/css/template.css | 116 +++++++++++++++++++++++++++++++++ site/templates/game.html | 84 ++++++++++++++++++++++++ site/templates/gamelist.html | 34 ++++++++++ site/templates/home.html | 25 +++++++ site/templates/navigation.html | 25 +++++++ 19 files changed, 743 insertions(+) create mode 100644 .gitignore create mode 100644 api/api.conf create mode 100644 api/api.py create mode 100644 api/modules/db_connect.py create mode 100644 api/modules/functions.py create mode 100644 api/modules/game.py create mode 100644 api/modules/gamelist.py create mode 100644 site/index.py create mode 100644 site/language/en_US.json create mode 100644 site/language/sv_SE.json create mode 100644 site/modules/game.py create mode 100644 site/modules/gamelist.py create mode 100644 site/modules/init.py create mode 100644 site/site.conf create mode 100644 site/static/css/template.css create mode 100644 site/templates/game.html create mode 100644 site/templates/gamelist.html create mode 100644 site/templates/home.html create mode 100644 site/templates/navigation.html diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ec5d4af --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +__pycache__/ +*.py[cod] +api/devapi.conf +site/devsite.conf diff --git a/api/api.conf b/api/api.conf new file mode 100644 index 0000000..63943bf --- /dev/null +++ b/api/api.conf @@ -0,0 +1,11 @@ +[Database] +db_type = MySQL +host = 172.29.29.15 +user = user +pass = pass +db_name = gamedistrosys + +[Running] +host = localhost +port = 5501 + diff --git a/api/api.py b/api/api.py new file mode 100644 index 0000000..2dc0a8f --- /dev/null +++ b/api/api.py @@ -0,0 +1,48 @@ +from flask import request, jsonify, Flask +from flask.json import JSONEncoder +from datetime import date +import modules.db_connect + + +class MyJSONEncoder(JSONEncoder): + def default(self, o): + if isinstance(o, date): + return o.isoformat() + + return super().default(o) + + +class MyFlask(Flask): + json_encoder = MyJSONEncoder + + +app = MyFlask(__name__) +modules.db_connect.init() +import modules.gamelist # noqa: E402 +import modules.game # noqa: E402 + +# Important initialization stuff +config = modules.db_connect.config + + +# Just a simple homepage, nothing fancy +@app.route('/', methods=['GET']) +def home(): + if 'id' not in request.args: + return "

BCNS Game Distribution System

API-endpoint

" + else: + return jsonify("I see you're a man of culture as well") + + +# Fetch a list of one users owned vehicles +@app.route('//vehiclelist', methods=['GET']) +def user_vehiclelist(userid): + + return False + + +if __name__ == '__main__': + app.config['JSON_AS_ASCII'] = False + app.run( + host=config.get('Running', 'host'), + port=config.get('Running', 'Port')) diff --git a/api/modules/db_connect.py b/api/modules/db_connect.py new file mode 100644 index 0000000..5c9df0b --- /dev/null +++ b/api/modules/db_connect.py @@ -0,0 +1,34 @@ +import argparse +import configparser +import os +import inspect + + +def init(): + global config + + # 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() + + 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')) diff --git a/api/modules/functions.py b/api/modules/functions.py new file mode 100644 index 0000000..2a0a172 --- /dev/null +++ b/api/modules/functions.py @@ -0,0 +1,20 @@ +from io import BytesIO +from PIL import Image +import base64 + + +# Reducing the size of an artwork/image (PNG to JPEG) and base64-encode it. +# imagepath = full file image path +# expectedsize = medium or thumbnail +def reduceart(imagepath, expectedsize): + image = Image.open(imagepath) + rgb_image = image.convert("RGB") + with BytesIO() as f: + rgb_image.save(f, format="JPEG") + f.seek(0) + + if expectedsize == 'thumbnail': + rgb_image.thumbnail([90,90]) + rgb_image.save(f, format="JPEG") + f.seek(0) + return base64.b64encode(f.getvalue()).decode() diff --git a/api/modules/game.py b/api/modules/game.py new file mode 100644 index 0000000..378297b --- /dev/null +++ b/api/modules/game.py @@ -0,0 +1,65 @@ +from __main__ import app +from flask import jsonify, request +import base64 +import json +import modules.db_connect +from modules.functions import reduceart +import os + +contentpath = modules.db_connect.contentpath() + + +# Fetch and present all information from one _index.nfo-file +@app.route('/game', methods=['POST']) +def game(predefinednfo=False, return_dict=False, skip_artwork=False): + nfopath = "" + if predefinednfo is not False: + nfopath = predefinednfo + else: + nfopath = base64.b64decode(request.json).decode() + nfo = json.load(open(nfopath, 'r')) + nfo['path'] = base64.b64encode(nfopath.encode('utf-8')).decode() + + # Add front cover artwork in medium size + artpath = os.path.dirname(nfopath)+'/art/' + i = 0 + for art in nfo['game']['artwork']: + if skip_artwork is False: + nfo['game']['artwork'][i]['data'] = reduceart( + artpath+art['filename'], 'thumbnail') + if art['type'] == 'front': + nfo['game']['displayimage'] = reduceart( + artpath+art['filename'], 'thumbnail') + i += 1 + + if return_dict is False: + return jsonify(nfo) + return nfo + + +# Fetch all artwork from a given _index.nfo-file +@app.route('/game/artwork/size/', methods=['POST']) +@app.route('/game/artwork', methods=['POST']) +def artwork(size='max'): + f = open(request.json['nfo'], 'r') + nfo = json.load(f) + + artpath = os.path.dirname(request.json['nfo'])+'/art/' + artlist = [] + for art in nfo['game']['artwork']: + img = "" + + # If max size, send image as-is + if size == 'max': + with open(artpath+art['filename'], mode='rb') as file: + img = file.read() + art['img'] = base64.b64encode(img).decode() + + # Changes filetype to jpeg for size reduction. This also drops + # Alpha-channel + else: + art['img'] = reduceart(artpath+art['filename'], size) + + artlist.append(art) + + return jsonify(artlist) diff --git a/api/modules/gamelist.py b/api/modules/gamelist.py new file mode 100644 index 0000000..f6065d0 --- /dev/null +++ b/api/modules/gamelist.py @@ -0,0 +1,24 @@ +from __main__ import app +from flask import jsonify, request +import modules.db_connect +import modules.game +import glob +import json +import base64 + +contentpath = modules.db_connect.contentpath() + +# Fetch all _index.nfo-files present in given path and corresponding data +@app.route('/gamelist', methods=['GET']) +def gamelist(): + + nfolist = list(dict.fromkeys(glob.glob(str(contentpath)+'/**/**/*_index.nfo',recursive=True))) + + gamelist = [] + for nfo in nfolist: + # f = open(nfo, encoding="UTF-8") + game = modules.game.game(nfo, True, True) + # game['game']['path'] = base64.b64encode(nfo.encode('utf-8')).decode() + gamelist.append(game) + + return jsonify(gamelist) diff --git a/site/index.py b/site/index.py new file mode 100644 index 0000000..e5daa74 --- /dev/null +++ b/site/index.py @@ -0,0 +1,76 @@ +from datetime import date +from flask import render_template, Flask, request +from flask.json import JSONEncoder +import modules.init +import os +import inspect +import json +import glob + + +class MyJSONEncoder(JSONEncoder): + def default(self, o): + if isinstance(o, date): + return o.isoformat() + + return super().default(o) + + +class MyFlask(Flask): + json_encoder = MyJSONEncoder + + +app = MyFlask(__name__) +modules.init.init() +import modules.gamelist # noqa: E402 +import modules.game # noqa: E402 + +# Init stuff +config = modules.init.config +host_endpoint = modules.init.host_endpoint() +languages = modules.init.get_languages() +# App-server-stuff +app.config["DEBUG"] = True + + +# Check if valid language code is set. If not, return app default +def lang(lang_code): + if lang_code not in languages: + return app_language + return lang_code + + +@app.route("/", methods=["GET", "POST"]) +@app.route("//", methods=["GET", "POST"]) +def home(lang_code=False): + lang_code = lang(lang_code) + if request.form: + lang_code = request.form["language_select"] + return render_template('home.html', **languages[lang_code], + lang_code=lang_code, + languages=languages.keys()) + + +# Initiation +if __name__ == '__main__': + app.config['JSON_AS_ASCII'] = False + + # Language stuff + app_language = 'en_US' + date_format = "%Y-%m-%d %H:%M:%S" + + languages_path = os.path.dirname( + os.path.abspath(inspect.getfile( + inspect.currentframe()))) + language_list = glob.glob(languages_path + "/language/*.json") + print("### Loaded languages") + for language in language_list: + filename = os.path.basename(language) + lang_code = filename.split('.')[0] + + with open(language, 'r', encoding='utf-8') as file: + print("# ", lang_code) + languages[lang_code] = json.loads(file.read()) + app.run( + host=config.get('Main', 'host'), + port=config.get('Main', 'port')) diff --git a/site/language/en_US.json b/site/language/en_US.json new file mode 100644 index 0000000..c6a79e2 --- /dev/null +++ b/site/language/en_US.json @@ -0,0 +1,35 @@ +{ + "lang_home_body_header": "Hello and welcome", + "lang_home_change_language": "Change language", + "lang_home_change_language_button": "CHange", + "lang_navigation_home": "Home", + "lang_navigation_gamelist": "Game list", + "lang_gamelist_title": "Gamelist", + "lang_gamelist_body_header": "Gamelist", + "lang_gamelist_body_ingress": "Here there be a bunch of games listed", + "lang_game_title": "Game", + "lang_game_body_header": "Game", + "lang_game_body_ingress": "Here there be info about games n stuff", + "lang_game_plot": "Plot", + "lang_game_download_header": "Download game", + "lang_game_download_ingress": "Here you can download the game using either torrent file or a magnet-link!", + "lang_game_download_torrent_button": "Download", + "lang_game_download_magnet_link": "Magnet link", + "lang_game_artwork": "Artwork", + "lang_game_year": "Release date", + "lang_game_genre": "Genre", + "lang_game_originaltitle": "Original title", + "lang_game_developer": "Developer", + "lang_game_playermodes": "Player modes", + "lang_game_collab": "In collaboration with", + "lang_game_credits": "Credits", + "lang_game_rating": "Rating", + "lang_game_recommendedspecs": "Recommended specs.", + "lang_game_systemrequirements": "System requirements", + "lang_game_images_header": "Images", + "lang_game_disctitle": "Disc name", + "lang_game_disctype": "Medium", + "lang_game_image_files": "Image files", + "lang_game_cd_key": "CD-Key", + "lang_game_linuxinstructions": "Linux instructions" +} diff --git a/site/language/sv_SE.json b/site/language/sv_SE.json new file mode 100644 index 0000000..03f7f32 --- /dev/null +++ b/site/language/sv_SE.json @@ -0,0 +1,7 @@ +{ + "lang_home_body_header": "Hej och välkommen", + "lang_home_change_language": "Byt språk", + "lang_home_change_language_button": "Byt", + "lang_navigation_home": "Hem", + "lang_navigation_gamelist": "Spellista" +} diff --git a/site/modules/game.py b/site/modules/game.py new file mode 100644 index 0000000..e51724f --- /dev/null +++ b/site/modules/game.py @@ -0,0 +1,44 @@ +from __main__ import app +import json +import requests +from flask import render_template, request +import modules.init + +host_endpoint = modules.init.host_endpoint() +languages = modules.init.get_languages() +app_language = modules.init.app_language + + +# Check if valid language code is set. If not, return app default +def lang(lang_code): + if lang_code not in languages: + return app_language + return lang_code + + +# Show game +@app.route("//game") +def game(lang_code): + lang_code = lang(lang_code) + gamepath = request.args.get("gamepath") + + game = json.loads((requests.post( + host_endpoint + '/game', json=gamepath).content).decode()) + +# game['game']['plot'] = game['game']['plot'].replace("\\n", "
") + game['game']['plot'] = game['game']['plot'].split('\\n') + game['game']['linuxinstructions'] = game['game']['linuxinstructions'].split('\\n') + return render_template('game.html', game=game, + **languages[lang_code], lang_code=lang_code) + + +# Show game artwork +@app.route("//game/artwork") +def artwork(lang_code): + pass + + +# Download a game +@app.route("//game/download") +def download(lang_code): + pass diff --git a/site/modules/gamelist.py b/site/modules/gamelist.py new file mode 100644 index 0000000..06fa2a8 --- /dev/null +++ b/site/modules/gamelist.py @@ -0,0 +1,29 @@ +from __main__ import app +import datetime +import json +import requests +from flask import render_template, redirect, request, url_for +import modules.init + +host_endpoint = modules.init.host_endpoint() +languages = modules.init.get_languages() +app_language = modules.init.app_language + + +# Check if valid language code is set. If not, return app default +def lang(lang_code): + if lang_code not in languages: + return app_language + return lang_code + + +# Show gamelist +@app.route("//gamelist") +# @app.route("/gamelist") +def gamelist(lang_code): + lang_code = lang(lang_code) + glist = json.loads((requests.get( + host_endpoint + '/gamelist').content).decode()) + + return render_template('gamelist.html', gamelist=glist, + **languages[lang_code], lang_code=lang_code) diff --git a/site/modules/init.py b/site/modules/init.py new file mode 100644 index 0000000..0e08907 --- /dev/null +++ b/site/modules/init.py @@ -0,0 +1,54 @@ +import argparse +import configparser +import os +import inspect + +global languages +languages = {} + + +def set_languages(languages_dict): + global languages + languages = languages_dict + return True + + +def get_languages(): + global languages + return languages + + +def init(): + global config + global app_language + + app_language = 'en_US' + # 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() + + if args.environment == 'dev': + print("Using devsite.conf") + configfile = '/../devsite.conf' + elif args.environment == 'test': + print("Using testsite.conf") + configfile = '/../testsite.conf' + else: + print("Using site.conf") + configfile = '/../site.conf' + + config = configparser.RawConfigParser() + config.read(os.path.dirname( + os.path.abspath(inspect.getfile( + inspect.currentframe()))) + configfile) + + +# Constructor for the API-endpoint +def host_endpoint(): + return ('http://' + config.get('API', 'host') + ':' + + config.get('API', 'port')) diff --git a/site/site.conf b/site/site.conf new file mode 100644 index 0000000..28a8a81 --- /dev/null +++ b/site/site.conf @@ -0,0 +1,8 @@ +[Main] +hostip = 172.29.29.31 +debug = True +host = localhost +port = 5500 + +[API] +apihost = 172.29.29.17 diff --git a/site/static/css/template.css b/site/static/css/template.css new file mode 100644 index 0000000..18c2552 --- /dev/null +++ b/site/static/css/template.css @@ -0,0 +1,116 @@ +body { + margin: 0; + padding: 0; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + color: #444; +} +/* +* Formatting the header area +*/ +header { + background-color: #DFB887; + height: 35px; + width: 100%; + opacity: .9; + margin-bottom: 10px; +} +header h1.logo { + margin: 0; + font-size: 1.7em; + color: #fff; + text-transform: uppercase; + float: left; +} +header h1.logo:hover { + color: #fff; + text-decoration: none; +} +/* +* Centering the body content +*/ +.container { + width: 1200px; + margin: 0 auto; +} +div.home { + padding: 10px 0 30px 0; + background-color: #E6E6FA; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} +div.about { + padding: 10px 0 30px 0; + background-color: #E6E6FA; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} +h2 { + font-size: 3em; + margin-top: 40px; + text-align: center; + letter-spacing: -2px; +} +h3 { + font-size: 1.7em; + font-weight: 100; + margin-top: 30px; + text-align: center; + letter-spacing: -1px; + color: #999; +} +.menu { + float: right; + margin-top: 8px; +} +.menu li { + display: inline; +} +.menu li + li { + margin-left: 35px; +} +.menu li a { + color: #444; + text-decoration: none; +} +.fleet { + border: 1px solid black; +} +.vehicle_basic_info { + text-align: left; + border: 1px solid black; +} +.vehicle_owner_history { + position: right; + text-align: left; + border: 1px solid black; +} +.last_inspection { + position: right; + text-align: left; + border: 1px solid black; +} +.general_table { + position: right; + text-align: left; + border: 1px solid black; +} +.tooltip { + position: relative; +} +.tooltip .tooltiptext { + visibility: hidden; + width: 120px; + background-color: black; + color: #fff; + text-align: center; + border-radius: 6px; + padding: 5px 0; + + position: absolute: + z-index: 1; +} +.tooltip:hover .tooltiptext { + visibility: visible; +} diff --git a/site/templates/game.html b/site/templates/game.html new file mode 100644 index 0000000..a20193d --- /dev/null +++ b/site/templates/game.html @@ -0,0 +1,84 @@ + + + + {{lang_game_title}} + + + + {% extends "navigation.html" %} + {% block content %} +

{{lang_game_body_header}}

+

{{lang_game_body_inress}}

+

{{game.game.title}}

+

{{lang_game_plot}}

+ {% for part in game.game.plot %} +

{{part}}

+ {% endfor %} +

{{lang_game_download_header}}

+

{{lang_game_download_ingress}}

+ +

{{lang_game_download_magnet_link}}

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
{{lang_game_artwork}}
+ {% for part in game.game.artwork %} + {{part.type}} + {% endfor %} +
{{lang_game_year}}{{game.game.year}}
{{lang_game_genre}}{{game.game.genre}}
{{lang_game_originaltitle}}{{game.game.originaltitle}}
{{lang_game_developer}}{{game.game.developer}}
{{lang_game_playermodes}}{{game.game.playermodes}}
{{lang_game_collab}}{{game.game.collab}}
{{lang_game_credits}}{{game.game.credits}}
{{lang_game_rating}}{{game.game.rating}}
{{lang_game_recommendedspecs}}{{game.game.recommendedspecs}}
{{lang_game_systemrequirements}}{{game.game.systemrequirements}}
+

{{lang_game_images_header}}

+ {% for part in game.game.imagelist %} +

{{lang_game_disctitle}}: {{part.title}}

+

{{lang_game_disctype}}: {{part.type}}

+

{{lang_game_image_files}}: + {% for partname in part.name %} + {{partname}} + {% endfor %} +

+
+ {% endfor %} + + {% if game.game.cdkey != "" %} +

{{lang_game_cd_key}}

+

{{game.game.cdkey}}

+ {% endif %} + +

{{lang_game_linuxinstructions}}

+ {% for part in game.game.linuxinstructions %} +

{{part}}

+ {% endfor %} + {% endblock %} + + diff --git a/site/templates/gamelist.html b/site/templates/gamelist.html new file mode 100644 index 0000000..80f2b16 --- /dev/null +++ b/site/templates/gamelist.html @@ -0,0 +1,34 @@ + + + + {{lang_gamelist_title}} + + + + {% extends "navigation.html" %} + {% block content %} +

{{lang_gamelist_body_header}}

+

{{lang_gamelist_body_ingress}}

+ + + + + + + + + + {% for item in gamelist %} + + + + + + + + + {% endfor %} +
{{lang_game_artwork}}{{lang_game_title}}{{lang_game_year}}{{lang_game_genre}}{{lang_game_developer}}{{lang_game_playermodes}}
{{item.game.title}}{{item.game.year}}{{item.game.genre}}{{item.game.developer}}{{item.game.playermodes}}
+ {% endblock %} + + diff --git a/site/templates/home.html b/site/templates/home.html new file mode 100644 index 0000000..d4f4655 --- /dev/null +++ b/site/templates/home.html @@ -0,0 +1,25 @@ + + + + BCNS + + + {% extends "navigation.html" %} + {% block content %} +

{{lang_home_body_header}}

+
+

{{lang_home_change_language}}

+ +