Uploaded to gitea

This commit is contained in:
odecif 2022-10-26 09:17:21 +02:00
parent c23a61a0bb
commit 0f74212063
19 changed files with 743 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
__pycache__/
*.py[cod]
api/devapi.conf
site/devsite.conf

11
api/api.conf Normal file
View File

@ -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

48
api/api.py Normal file
View File

@ -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 "<h1>BCNS Game Distribution System</h1><p>API-endpoint</p>"
else:
return jsonify("I see you're a man of culture as well")
# Fetch a list of one users owned vehicles
@app.route('/<userid>/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'))

34
api/modules/db_connect.py Normal file
View File

@ -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'))

20
api/modules/functions.py Normal file
View File

@ -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()

65
api/modules/game.py Normal file
View File

@ -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/<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)

24
api/modules/gamelist.py Normal file
View File

@ -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)

76
site/index.py Normal file
View File

@ -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("/<lang_code>/", 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'))

35
site/language/en_US.json Normal file
View File

@ -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"
}

7
site/language/sv_SE.json Normal file
View File

@ -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"
}

44
site/modules/game.py Normal file
View File

@ -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("/<lang_code>/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", "<br />")
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("/<lang_code>/game/artwork")
def artwork(lang_code):
pass
# Download a game
@app.route("/<lang_code>/game/download")
def download(lang_code):
pass

29
site/modules/gamelist.py Normal file
View File

@ -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("/<lang_code>/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)

54
site/modules/init.py Normal file
View File

@ -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'))

8
site/site.conf Normal file
View File

@ -0,0 +1,8 @@
[Main]
hostip = 172.29.29.31
debug = True
host = localhost
port = 5500
[API]
apihost = 172.29.29.17

View File

@ -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;
}

84
site/templates/game.html Normal file
View File

@ -0,0 +1,84 @@
<!DOCTYPE html>
<html>
<head>
<title>{{lang_game_title}}</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/template.css') }}">
</head>
<body>
{% extends "navigation.html" %}
{% block content %}
<h1>{{lang_game_body_header}}</h1>
<p>{{lang_game_body_inress}}</p>
<h2>{{game.game.title}}</h2>
<h3>{{lang_game_plot}}</h3>
{% for part in game.game.plot %}
<p>{{part}}</p>
{% endfor %}
<h3>{{lang_game_download_header}}</h3>
<p>{{lang_game_download_ingress}}</p>
<a href="{{ url_for('download', gamepath=game.path, lang_code=lang_code)}}"><button type="button">{{lang_game_download_torrent_button}}</button></a>
<p><a href="">{{lang_game_download_magnet_link}}</a></p>
<table class="game">
<tr>
<th>{{lang_game_artwork}}</th>
<td><div class="tooltip">
{% for part in game.game.artwork %}
<span class="tooltiptext">{{part.type}}</span><a href="{{ url_for('artwork', gamepath=game.path, art=part.filename, lang_code=lang_code)}}"><img src="data:image/jpeg;base64,{{part.data}}"></img></a>
{% endfor %}
</div></td>
</tr><tr>
</tr><tr>
<th>{{lang_game_year}}</th>
<td>{{game.game.year}}</td>
</tr><tr>
<th>{{lang_game_genre}}</th>
<td>{{game.game.genre}}</td>
</tr><tr>
<th>{{lang_game_originaltitle}}</th>
<td>{{game.game.originaltitle}}</td>
</tr><tr>
<th>{{lang_game_developer}}</th>
<td>{{game.game.developer}}</td>
</tr><tr>
<th>{{lang_game_playermodes}}</th>
<td>{{game.game.playermodes}}</td>
</tr><tr>
<th>{{lang_game_collab}}</th>
<td>{{game.game.collab}}</td>
</tr><tr>
<th>{{lang_game_credits}}</th>
<td>{{game.game.credits}}</td>
</tr><tr>
<th>{{lang_game_rating}}</th>
<td>{{game.game.rating}}</td>
</tr><tr>
<th>{{lang_game_recommendedspecs}}</th>
<td>{{game.game.recommendedspecs}}</td>
</tr><tr>
<th>{{lang_game_systemrequirements}}</th>
<td>{{game.game.systemrequirements}}</td>
</table>
<h3>{{lang_game_images_header}}</h3>
{% for part in game.game.imagelist %}
<p>{{lang_game_disctitle}}: {{part.title}}</p>
<p>{{lang_game_disctype}}: {{part.type}}</p>
<p>{{lang_game_image_files}}:
{% for partname in part.name %}
{{partname}}
{% endfor %}
</p>
<br />
{% endfor %}
{% if game.game.cdkey != "" %}
<h3>{{lang_game_cd_key}}</h3>
<p>{{game.game.cdkey}}</p>
{% endif %}
<h3>{{lang_game_linuxinstructions}}</h3>
{% for part in game.game.linuxinstructions %}
<p>{{part}}</p>
{% endfor %}
{% endblock %}
</body>
</html>

View File

@ -0,0 +1,34 @@
<!DOCTYPE html>
<html>
<head>
<title>{{lang_gamelist_title}}</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/template.css') }}">
</head>
<body>
{% extends "navigation.html" %}
{% block content %}
<h1>{{lang_gamelist_body_header}}</h1>
<p>{{lang_gamelist_body_ingress}}</p>
<table class="game">
<tr>
<th>{{lang_game_artwork}}</th>
<th>{{lang_game_title}}</th>
<th>{{lang_game_year}}</th>
<th>{{lang_game_genre}}</th>
<th>{{lang_game_developer}}</th>
<th>{{lang_game_playermodes}}</th>
</tr>
{% for item in gamelist %}
<tr>
<td><a href="{{ url_for('game', gamepath=item.path, lang_code=lang_code)}}"><img src="data:image/jpeg;base64,{{item.game.displayimage}}"></img></a></td>
<td><a href="{{ url_for('game', gamepath=item.path, lang_code=lang_code)}}">{{item.game.title}}</a></td>
<td>{{item.game.year}}</td>
<td>{{item.game.genre}}</td>
<td>{{item.game.developer}}</td>
<td>{{item.game.playermodes}}</td>
</tr>
{% endfor %}
</table>
{% endblock %}
</body>
</html>

25
site/templates/home.html Normal file
View File

@ -0,0 +1,25 @@
<!DOCTYPE html>
<html>
<head>
<title>BCNS</title>
</head>
<body>
{% extends "navigation.html" %}
{% block content %}
<h1>{{lang_home_body_header}}</h1>
<form action="{{url_for('home')}}" method="POST">
<p>{{lang_home_change_language}}</p>
<select name="language_select">
{% for language in languages %}
{% if lang_code == language %}
<option selected>{{language}}</option>
{% else %}
<option>{{language}}</option>
{% endif %}
{% endfor %}
</select>
<button type="submit" name="change_language">{{lang_home_change_language_button}}</input>
</form>
{% endblock %}
</body>
</html>

View File

@ -0,0 +1,25 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>BCNS</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/template.css') }}">
</head>
<body>
<header>
<div class="container">
<h1 class="logo">BCNS Game Distribution System</h1>
<strong>
<nav>
<ul class="menu">
<li><a href="{{ url_for('home', lang_code=lang_code) }}">{{lang_navigation_home}}</a></li>
<li><a href="{{ url_for('gamelist', lang_code=lang_code) }}">{{lang_navigation_gamelist}}</a></li>
</ul>
</nav>
</strong>
</div>
</header>
{% block content %}
{% endblock %}
</body>
</html>