Change track through web page
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -174,3 +174,6 @@ cython_debug/
|
|||||||
# PyPI configuration file
|
# PyPI configuration file
|
||||||
.pypirc
|
.pypirc
|
||||||
|
|
||||||
|
# paths for testing
|
||||||
|
content/
|
||||||
|
cfg/
|
||||||
|
|||||||
132
ac-server-scripts-api.py
Normal file
132
ac-server-scripts-api.py
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
from http.server import HTTPServer, BaseHTTPRequestHandler
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
from urllib.parse import parse_qs
|
||||||
|
from change_track import change_track, get_all_tracks, get_configs, get_preview_image, get_outline_image
|
||||||
|
|
||||||
|
class Handler(BaseHTTPRequestHandler):
|
||||||
|
def do_GET(self):
|
||||||
|
if (self.handle_GET_path()):
|
||||||
|
return
|
||||||
|
|
||||||
|
self.send_error(404)
|
||||||
|
|
||||||
|
def do_POST(self):
|
||||||
|
if (self.handle_POST_path()):
|
||||||
|
return
|
||||||
|
|
||||||
|
self.send_error(404)
|
||||||
|
|
||||||
|
def handle_GET_path(self) -> bool:
|
||||||
|
"""
|
||||||
|
handles the path entered in the GET request.
|
||||||
|
|
||||||
|
Returns: true if the calling function should return, false otherwise
|
||||||
|
"""
|
||||||
|
# Serve preview images
|
||||||
|
if self.path.startswith("/img/"):
|
||||||
|
parts = self.path.split("/")
|
||||||
|
parts = [p for p in parts if p] # remove empty
|
||||||
|
|
||||||
|
img_type = parts[1] if len(parts) > 1 else ""
|
||||||
|
|
||||||
|
track, config = self.extract_track_and_config(parts)
|
||||||
|
|
||||||
|
img_path = ""
|
||||||
|
if (img_type == "preview"):
|
||||||
|
img_path = get_preview_image(track, config)
|
||||||
|
elif (img_type == "outline"):
|
||||||
|
img_path = get_outline_image(track, config)
|
||||||
|
else:
|
||||||
|
self.send_error(404)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
self.send_image(img_path)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
# JSON track info
|
||||||
|
if self.path.startswith("/track/"):
|
||||||
|
track = self.path.replace("/track/", "").strip("/")
|
||||||
|
configs = get_configs(track)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
"track": track,
|
||||||
|
"configs": configs,
|
||||||
|
"image": f"/img/preview/{track}",
|
||||||
|
"outline": f"/img/outline/{track}"
|
||||||
|
}
|
||||||
|
|
||||||
|
self.send_response(200)
|
||||||
|
self.send_header("Content-type", "application/json")
|
||||||
|
self.end_headers()
|
||||||
|
self.wfile.write(json.dumps(data).encode())
|
||||||
|
return True
|
||||||
|
|
||||||
|
if self.path == "/" or self.path == "/index.html":
|
||||||
|
with open("index.html", "r") as f:
|
||||||
|
html = f.read()
|
||||||
|
|
||||||
|
tracks = get_all_tracks()
|
||||||
|
track_options = "".join([f'<option value="{t}">{t}</option>' for t in tracks])
|
||||||
|
|
||||||
|
html = html.replace("{{tracks}}", track_options)
|
||||||
|
html = html.replace("{{configs}}", "") # empty at load
|
||||||
|
|
||||||
|
self.send_response(200)
|
||||||
|
self.send_header("Content-type", "text/html")
|
||||||
|
self.end_headers()
|
||||||
|
self.wfile.write(html.encode())
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def handle_POST_path(self):
|
||||||
|
if (self.path.startswith("/changetrack/")):
|
||||||
|
parts = self.path.split("/")
|
||||||
|
parts = [p for p in parts if p] # remove empty
|
||||||
|
track, config = self.extract_track_and_config(parts, 1)
|
||||||
|
print(f"Changing track to '{track}' with config '{config}'")
|
||||||
|
|
||||||
|
success, message = change_track(track, config)
|
||||||
|
print(message)
|
||||||
|
if success:
|
||||||
|
self.send_response(200)
|
||||||
|
self.send_header("Content-type", "application/json")
|
||||||
|
self.end_headers()
|
||||||
|
response = {"status": "success", "message": message}
|
||||||
|
self.wfile.write(json.dumps(response).encode())
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
self.send_response(400)
|
||||||
|
self.send_header("Content-type", "application/json")
|
||||||
|
self.end_headers()
|
||||||
|
response = {"status": "error", "message": message}
|
||||||
|
self.wfile.write(json.dumps(response).encode())
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def send_image(self, img_path: str):
|
||||||
|
if os.path.exists(img_path):
|
||||||
|
self.send_response(200)
|
||||||
|
self.send_header("Content-type", "image/png")
|
||||||
|
self.end_headers()
|
||||||
|
with open(img_path, "rb") as f:
|
||||||
|
self.wfile.write(f.read())
|
||||||
|
else:
|
||||||
|
self.send_error(404)
|
||||||
|
|
||||||
|
def extract_track_and_config(self, parts: list, track_index: int = 2) -> tuple[str, str]:
|
||||||
|
print(parts)
|
||||||
|
track = parts[track_index] if len(parts) > track_index else ""
|
||||||
|
config = parts[track_index + 1] if len(parts) > track_index + 1 else ""
|
||||||
|
|
||||||
|
return track, config
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
server = HTTPServer(("0.0.0.0", 10303), Handler)
|
||||||
|
print("Server running on port 10303")
|
||||||
|
server.serve_forever()
|
||||||
71
change_track.py
Normal file
71
change_track.py
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
# CONTENT_FOLDER = "/home/sem/assetto-corsa/content"
|
||||||
|
CONFIG_FILE = "server_cfg.ini"
|
||||||
|
# CONFIG_PATH = "/home/sem/assetto-corsa/cfg"
|
||||||
|
CONTENT_FOLDER = "./content"
|
||||||
|
CONFIG_PATH = "./cfg"
|
||||||
|
|
||||||
|
TRACKS_FOLDER = os.path.join(CONTENT_FOLDER, "tracks")
|
||||||
|
|
||||||
|
def get_all_tracks():
|
||||||
|
return sorted([x.name for x in os.scandir(TRACKS_FOLDER) if x.is_dir()])
|
||||||
|
|
||||||
|
def get_preview_image(track: str, config: str = ""):
|
||||||
|
img_path = os.path.join(TRACKS_FOLDER, track, "ui", config, "preview.png")
|
||||||
|
if os.path.exists(img_path):
|
||||||
|
return img_path
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def get_outline_image(track: str, config: str = ""):
|
||||||
|
img_path = os.path.join(TRACKS_FOLDER, track, "ui", config, "outline.png")
|
||||||
|
if os.path.exists(img_path):
|
||||||
|
return img_path
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def get_configs(track) -> list[str]:
|
||||||
|
track_path = os.path.join(TRACKS_FOLDER, track);
|
||||||
|
configdirs = [x.path.replace(track_path + "/","") for x in os.scandir(track_path) if x.is_dir()]
|
||||||
|
configdirs.sort()
|
||||||
|
standard_folders = {'ai', 'ui', 'extension', 'data', 'skins','texture', 'sfx'}
|
||||||
|
for s in standard_folders:
|
||||||
|
if s in configdirs:
|
||||||
|
configdirs.remove(s)
|
||||||
|
if (len(configdirs) == 0):
|
||||||
|
print("===== The map you entered does not have a config! =====")
|
||||||
|
config = ""
|
||||||
|
return configdirs
|
||||||
|
|
||||||
|
def change_track(newname, config=""):
|
||||||
|
|
||||||
|
print(f"handling track change for {newname} with config {config}")
|
||||||
|
|
||||||
|
if newname not in get_all_tracks():
|
||||||
|
return False, f"Track '{newname}' does not exist!"
|
||||||
|
|
||||||
|
new_content = ""
|
||||||
|
|
||||||
|
config_file = os.path.join(CONFIG_PATH, CONFIG_FILE)
|
||||||
|
with open(config_file,'r') as file:
|
||||||
|
for line in file:
|
||||||
|
line = line.strip()
|
||||||
|
if line.startswith("TRACK="):
|
||||||
|
new_content += "TRACK=" + str(newname)
|
||||||
|
print("changing line " + line + " to " + "TRACK=" + str(newname))
|
||||||
|
elif line.startswith("CONFIG_TRACK="):
|
||||||
|
if (len(config) > 1):
|
||||||
|
new_content +="CONFIG_TRACK=" + str(config)
|
||||||
|
print("changing line " + line + " to " + "CONFIG_TRACK=" + str(config))
|
||||||
|
else:
|
||||||
|
new_content += "CONFIG_TRACK="
|
||||||
|
print("no config entered, setting config line to CONFIG_TRACK=")
|
||||||
|
else:
|
||||||
|
new_content += line
|
||||||
|
new_content += "\n"
|
||||||
|
|
||||||
|
with open(config_file,'w') as newfile:
|
||||||
|
newfile.write(new_content)
|
||||||
|
|
||||||
|
return True, f"Changed track to: {newname}"
|
||||||
@@ -31,7 +31,7 @@ if str(newname) not in dirs:
|
|||||||
if resetname == False and len(config) > 0:
|
if resetname == False and len(config) > 0:
|
||||||
configdirs = [x.path.replace("/home/sem/assetto-corsa/content/tracks/" + newname + "/","") for x in os.scandir('/home/sem/assetto-corsa/content/tracks/' + newname + '/') if x.is_dir()]
|
configdirs = [x.path.replace("/home/sem/assetto-corsa/content/tracks/" + newname + "/","") for x in os.scandir('/home/sem/assetto-corsa/content/tracks/' + newname + '/') if x.is_dir()]
|
||||||
configdirs.sort()
|
configdirs.sort()
|
||||||
standard_folders = {'ai', 'ui', 'extension', 'data', 'skins','texture'}
|
standard_folders = {'ai', 'ui', 'extension', 'data', 'skins','texture', 'sfx'}
|
||||||
for s in standard_folders:
|
for s in standard_folders:
|
||||||
if s in configdirs:
|
if s in configdirs:
|
||||||
configdirs.remove(s)
|
configdirs.remove(s)
|
||||||
76
index.html
Normal file
76
index.html
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
<html>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h2>Choose a track</h2>
|
||||||
|
|
||||||
|
<select id="trackSelect">
|
||||||
|
{{tracks}}
|
||||||
|
</select>
|
||||||
|
<select id="configSelect">
|
||||||
|
{{configs}}
|
||||||
|
</select>
|
||||||
|
<button onclick="apply()">Apply</button>
|
||||||
|
|
||||||
|
<br><br>
|
||||||
|
|
||||||
|
<div class="parent">
|
||||||
|
<img id="preview" src="" width="400" style="border:1px solid #444">
|
||||||
|
<img id="outline" src="" width="400">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const trackSelect = document.getElementById("trackSelect");
|
||||||
|
const configSelect = document.getElementById("configSelect");
|
||||||
|
const previewImg = document.getElementById("preview");
|
||||||
|
const outlineImg = document.getElementById("outline");
|
||||||
|
|
||||||
|
|
||||||
|
function updateTrack() {
|
||||||
|
|
||||||
|
const URL = '/track/' + trackSelect.value;
|
||||||
|
fetch(URL, { method: "GET" })
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => {
|
||||||
|
console.log(data)
|
||||||
|
|
||||||
|
previewImg.src = data.image
|
||||||
|
outlineImg.src = data.outline
|
||||||
|
|
||||||
|
// Configs
|
||||||
|
configSelect.innerHTML = "";
|
||||||
|
data.configs.forEach(c => {
|
||||||
|
let opt = document.createElement("option");
|
||||||
|
opt.value = c;
|
||||||
|
opt.textContent = c;
|
||||||
|
configSelect.appendChild(opt);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update preview if a config exists
|
||||||
|
if (data.configs.length > 0) {
|
||||||
|
updateConfig();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateConfig() {
|
||||||
|
previewImg.src = "/img/preview/" + trackSelect.value + "/" + configSelect.value;
|
||||||
|
outlineImg.src = "/img/outline/" + trackSelect.value + "/" + configSelect.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function apply(){
|
||||||
|
const URL = '/changetrack/' + trackSelect.value + "/" + configSelect.value;
|
||||||
|
fetch(URL, { method: "POST" })
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => {
|
||||||
|
console.log(data)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
trackSelect.addEventListener("change", updateTrack);
|
||||||
|
configSelect.addEventListener("change", updateConfig);
|
||||||
|
|
||||||
|
window.onload = updateTrack;
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user