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