--- /dev/null
+- websockets for the api
+- update the doomsday clock repo
+- better solution to loading static data
+ - potentially in the database?
+ - could open it up for sqlite browser
#!.venv/bin/python
-from flask import Flask
+from flask_socketio import SocketIO, emit
from flask_httpauth import HTTPBasicAuth
from datetime import datetime, timezone
from os import path, environ, system
-from markupsafe import escape
from flask_cors import CORS
+from flask import Flask,request
import json
auth = HTTPBasicAuth()
app = Flask(__name__)
CORS(app)
+socketio = SocketIO(app, logger=True, engineio_logger=True)
try:
app.root_path = ROOT = environ["XMDV_PATH"]
# utils
app.before_request(teleshopping.check_database)
+# if the socket is connecting for the first time, send data
+@socketio.on('connect')
+def initial():
+ print("conected!")
+ request.method = "internal"
+ emit("apiUpdate", teleshopping.data(), json=True)
+
+
if __name__ == "__main__":
- app.run(host='127.0.0.1', port=8000, debug=True)
+ socketio.run(app, host='127.0.0.1', port=8000, debug=True)
flask_httpauth
+flask_socketio
markupsafe
flask_cors
werkzeug
--- /dev/null
+/*
+CSS stylesheet for styling the HUD's including the '/hud' page and the
+'/autocue' page
+*/
+
+body {
+ font-family: sans-serif;
+ color: white;
+ background-color: black;
+}
+
+#slider {
+ position: fixed;
+ top: 10px;
+ left: 10px;
+}
+
+#notes, #message {
+ margin: 20px;
+}
+
+#timer {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ margin: 20px;
+}
+
+#numbers {
+ position: absolute;
+ top: 20px;
+ left: 0;
+ margin: 20px;
+}
+#discountBox {
+ opacity: 0;
+ transition: opacity 1s;
+}
+#discountBox.show {
+ opacity: 1;
+}
--- /dev/null
+export function makeTime(end, offset, strike) {
+ let current = Math.round((Date.now() + offset) / 1000);
+ var time = end - current;
+
+ if (Math.sign(time) == -1) {time = 0;}
+ var minutes = Math.floor(time / 60);
+ var seconds = (time - (minutes * 60));
+
+ var minutesString = minutes.toString().padStart(2, "0");
+ var secondsString = seconds.toString().padStart(2, "0");
+ if (strike) {
+ return `${minutesString}:${secondsString}`;
+ } else {
+ return `<s>${minutesString}:${secondsString}</s>`;
+ }
+}
+from flask import Flask, Response, request, render_template, send_from_directory
from math import radians, cos, sin
+from .api.data import data as api
+from flask_socketio import emit
+from markupsafe import escape
+from os import environ
+import sqlite3
+import json
INCREMENT = 18
+ROOT = environ["XMDV_PATH"]
+
+with open(f"{ROOT}/static/static.json", "r", encoding="utf-8") as f:
+ static_data = json.loads(f.read())
+
+with open(f"{ROOT}/static/info.json", "r", encoding="utf-8") as f:
+ static_info = json.loads(f.read())
def admin():
return Response(render_template("admin.html", mimetype="text/html"))
else: aggrigate_data.update({key: post_data[key]})
- with sqlite3.connect(path.join(app.root_path, "data.db")) as connection:
+ with sqlite3.connect(f"{ROOT}/data.db") as connection:
cursor = connection.cursor()
result = cursor.execute("SELECT * FROM pragma_table_info('state');").fetchall()[1:]
WHERE id = 1;
"""
- print(query)
-
cursor.execute(query)
connection.commit()
- request.method = "internal" # set method to internal so that the api call returns only the python object
+ request.method = "internal" # set method to internal so that the api call returns only the python object
+ emit('apiUpdate', api(), broadcast=True, namespace="/")
+
+ else:
+ request.method = "internal" # set method to internal so that the api call returns only the python object
+
if page == "clock":
coords = [{
"i":i,
else:
return "", 404
+def handle_message(message):
+ send(message)
+
def items():
if request.method == "GET":
return jsonify(static_data)
<meta charset="utf-8">
<title>XMDV Autocue</title>
<link rel="icon" type="image/x-icon" href="/static/assets/star3.svg">
- <style>
-body {
- font-family: sans-serif;
- color: white;
- background-color: black;
-}
-#slider {
- position: fixed;
- top: 10px;
- left: 10px;
-}
-#notes, #message {
- margin: 20px;
-}
-#timer {
- position: absolute;
- bottom: 0;
- left: 0;
- margin: 20px;
-}
- </style>
- <script>
+ <link rel="stylesheet" href="/static/display.css">
+ <script type="module">
-function makeTime(end, offset, strike) {
- let current = Math.round((Date.now() + offset) / 1000);
- var time = end - current;
+import { io } from "https://cdn.socket.io/4.8.1/socket.io.esm.min.js";
- if (Math.sign(time) == -1) {time = 0;}
- var minutes = Math.floor(time / 60);
- var seconds = (time - (minutes * 60));
+/*
+websocket updates when data is sent to the admin pannel, updating the autocue
+this eliminates the ovehead that comes from constantly polling the api.
+on connection, the system also sends the api data.
+*/
- var minutesString = minutes.toString().padStart(2, "0");
- var secondsString = seconds.toString().padStart(2, "0");
- if (strike) {
- return `${minutesString}:${secondsString}`;
- } else {
- return `<s>${minutesString}:${secondsString}</s>`;
- }
-}
+var socket = io('/');
+socket.on('apiUpdate', function (data) {
+ console.info("Recived data, updating...");
+ update(data);
+});
-// dynamically resize the UI
-function resize() {
- var slider = document.getElementById("slider");
+ </script>
+ </head>
+ <body>
+ <input type="range" min="30" max="100" value="45" id="slider">
+ <div style="font-size: 45px;" id="note">{{item.notes|safe}}</div>
+ <div style="font-size: 45px; color: yellow;" id="producer">{{data.note}}</div>
+
+ <script>
+
+// update text when slider changes
+var slider = document.getElementById("slider");
+slider.oninput = function (e) {
for (let id of ["note", "producer"]) {
- document.getElementById(id).style.fontSize = `${slider.value}px`;
+ document.getElementById(id).style.fontSize = `${this.value}px`;
}
}
-function update () {
+// update function called by the websocket handler
+function update ( data ) {
// fetch the item manifest and chache it
fetch("./api/items", {cache: "default"})
- .then(data => data.json())
- .then(data => data.items)
- .then(items => {
- // fetch the current state without cache
- fetch("./api", {cache: "no-store"})
- .then(data => data.json())
- .then(data => {
- let id = data.item_id;
+ .then(items => items.json())
+ .then(items => items.items)
+ .then(items => {
- // update the producer note
- const producer = document.getElementById("producer");
- producer.innerHTML = data.note.replaceAll("\n", "<br>");
+ // update the producer note
+ const producer = document.getElementById("producer");
+ producer.innerHTML = data.note.replaceAll("\n", "<br>");
- // update the item information
- const note = document.getElementById("note");
- note.innerHTML = items[id].notes;
- })
- });
-}
+ // update the item information
+ let id = data.item_id;
+ const note = document.getElementById("note");
+ note.innerHTML = items[id].notes;
-setInterval(resize, 50);
-setInterval(update, 1000);
+ });
+ }
</script>
- </head>
- <body onload="resize(); update();">
- <input type="range" min="30" max="100" value="45" id="slider">
- <div style="font-size: 50px;" id="note">{{item.notes|safe}}</div>
- <div style="font-size: 50px; color: yellow;" id="producer">{{data.note}}</div>
</body>
</html>
<head>
<meta charset="utf-8">
<title>XMDV Display</title>
- <style>
-body {
- font-family: sans-serif;
- color: white;
- background-color: black;
-}
-#slider {
- position: fixed;
- top: 10px;
- left: 10px;
-}
-body > div {
- position: absolute;
- top: 0;
- left: 0;
- margin: 20px;
-}
-#discountBox {
- opacity: 0;
- transition: opacity 1s;
-}
-#discountBox.show {
- opacity: 1;
-}
- </style>
- <script>
+ <link rel="icon" type="image/x-icon" href="/static/assets/star3.svg">
+ <link rel="stylesheet" href="/static/display.css">
+ </head>
+ <body style="font-size:80px;">
+ <input type="range" min="30" max="100" value="80" id="slider">
+ <div id="numbers">
+ T1: <span style="background-color: green;" id="timer_1">00:00</span> -
+ T2: <span style="background-color: purple;" id="timer_2">00:00</span> -
+ T3: <span style="background-color: blue;" id="timer_3">00:00</span><br>
+ Left: <span id="left">0</span> out of <span id="stock">0</span><br>
+ Start price: <span style="background-color:deeppink" id="price">0.00</span>
+ ⤓<span style="color:orange" id="gallery">00</span>%
+ ⤈<span style="color:red" id="cost">00</span>%<br>
+ <span id="discountBox">Discount: <span style="background-color:darkviolet"><span id="discount">00</span>%</span> (<em>Now <span id="currentPrice" style="background-color:darkviolet;">0.00</span></em>)</span>
+ </div>
+ <script type="module">
-function makeTime(end, offset, strike) {
- let current = Math.round((Date.now() + offset) / 1000);
- var time = end - current;
+import { makeTime } from "../static/utils.js";
+import { io } from "https://cdn.socket.io/4.8.1/socket.io.esm.min.js";
- if (Math.sign(time) == -1) {time = 0;}
- var minutes = Math.floor(time / 60);
- var seconds = (time - (minutes * 60));
+/*
+websocket updates when data is sent to the admin pannel, updating the autocue
+this eliminates the ovehead that comes from constantly polling the api.
+on connection, the system also sends the api data.
+*/
- var minutesString = minutes.toString().padStart(2, "0");
- var secondsString = seconds.toString().padStart(2, "0");
- if (strike) {
- return `${minutesString}:${secondsString}`;
- } else {
- return `<s>${minutesString}:${secondsString}</s>`;
- }
-}
+var socket = io('/');
+socket.on('apiUpdate', function (data) {
+ console.info("Recived data, updating...");
+ update(data);
+});
-// dynamically resize the UI
-function resize() {
- var slider = document.getElementById("slider");
- document.getElementsByTagName("body")[0].style.fontSize = `${slider.value}px`;
+// update text when slider changes
+var slider = document.getElementById("slider");
+slider.oninput = function (e) {
+ document.getElementsByTagName("body")[0].style.fontSize = `${this.value}px`;
}
let rep;
-
-function update () {
+function update ( data ) {
// fetch the item manifest and chache it
fetch("./api/items", {cache: "default"})
- .then(data => data.json())
- .then(data => data.items)
- .then(items => {
- // fetch the current state without cache
- fetch("./api", {cache: "no-store"})
- .then(data => data.json())
- .then(data => {
- let id = data.item_id;
+ .then(items => items.json())
+ .then(items => items.items)
+ .then(items => {
+ let id = data.item_id;
- if (data.bool_number) { rep = "7"; }
- else { rep = "#####"; }
+ if (data.bool_number) { rep = "7"; }
+ else { rep = "#####"; }
- // update the total items and the items sold already
- document.getElementById("left").innerHTML = String(Math.round(
- items[id].stock_count * (data.percent_remaining / 100)
- )).replaceAll(rep, "█");
- document.getElementById("stock").innerHTML = String(items[id].stock_count).replaceAll(rep, "█");
+ // update the total items and the items sold already
+ document.getElementById("left").innerHTML = String(Math.round(
+ items[id].stock_count * (data.percent_remaining / 100)
+ )).replaceAll(rep, "█");
+ document.getElementById("stock").innerHTML = String(items[id].stock_count).replaceAll(rep, "█");
- // update the timers
- for (let t = 1; t <= 3; t++) {
- document.getElementById(`timer_${t}`).innerHTML = String(makeTime(
- data[`end_timer_${t}`],
- data['timer_offset'],
- data[`bool_timer_${t}`]
- )).replaceAll(rep, "█")
- }
+ // update the timers
+ for (let t = 1; t <= 3; t++) {
+ document.getElementById(`timer_${t}`).innerHTML = String(makeTime(
+ data[`end_timer_${t}`],
+ data['timer_offset'],
+ data[`bool_timer_${t}`]
+ )).replaceAll(rep, "█")
+ }
- // update the discount
- document.getElementById("discount").innerHTML = String(data.discount).replaceAll(rep, "█");
- document.getElementById("gallery").innerHTML = String(items[id].gallery_price * 100).replaceAll(rep, "█");
- document.getElementById("cost").innerHTML = String(items[id].cost_price * 100).replaceAll(rep, "█");
- if (data.discount != 0) {
- document.getElementById("discountBox").classList.add("show");
- } else {
- document.getElementById("discountBox").classList.remove("show");
- }
-
- // update the prices
- const price = document.getElementById("price");
- const currentPrice = document.getElementById("currentPrice");
- if (items[id].prefix) {
- price.innerHTML = `${items[id].currency}${String(items[id].origional_price).replaceAll(rep, "█")}`;
- currentPrice.innerHTML = `${items[id].currency}${String(Math.round(items[id].origional_price * data.discount) / 100).replaceAll(rep, "█")}`;
- } else {
- price.innerHTML = `${String(items[id].origional_price).replaceAll(rep, "█")}${items[id].currency}`;
- currentPrice.innerHTML = `${String(Math.round(items[id].origional_price * data.discount) / 100).replaceAll(rep, "█")}${items[id].currency}`;
- }
- })
- });
-}
+ // update the discount
+ document.getElementById("discount").innerHTML = String(data.discount).replaceAll(rep, "█");
+ document.getElementById("gallery").innerHTML = String(items[id].gallery_price * 100).replaceAll(rep, "█");
+ document.getElementById("cost").innerHTML = String(items[id].cost_price * 100).replaceAll(rep, "█");
+ if (data.discount != 0) {
+ document.getElementById("discountBox").classList.add("show");
+ } else {
+ document.getElementById("discountBox").classList.remove("show");
+ }
-setInterval(resize, 50);
-setInterval(update, 1000);
+ // update the prices
+ const price = document.getElementById("price");
+ const currentPrice = document.getElementById("currentPrice");
+ if (items[id].prefix) {
+ price.innerHTML = `${items[id].currency}${String(items[id].origional_price).replaceAll(rep, "█")}`;
+ currentPrice.innerHTML = `${items[id].currency}${String(Math.round(items[id].origional_price * data.discount) / 100).replaceAll(rep, "█")}`;
+ } else {
+ price.innerHTML = `${String(items[id].origional_price).replaceAll(rep, "█")}${items[id].currency}`;
+ currentPrice.innerHTML = `${String(Math.round(items[id].origional_price * data.discount) / 100).replaceAll(rep, "█")}${items[id].currency}`;
+ }
+ });
+ }
</script>
- </head>
- <body onload="resize(); update();">
- <input type="range" min="30" max="100" value="95" id="slider">
- <div>
- T1: <span style="background-color: green;" id="timer_1">00:00</span> -
- T2: <span style="background-color: purple;" id="timer_2">00:00</span> -
- T3: <span style="background-color: blue;" id="timer_3">00:00</span><br>
- Left: <span id="left">0</span> out of <span id="stock">0</span><br>
- Start price: <span style="background-color:deeppink" id="price">0.00</span>
- ⤓<span style="color:orange" id="gallery">00</span>%
- ⤈<span style="color:red" id="cost">00</span>%<br>
- <span id="discountBox">Discount: <span style="background-color:darkviolet"><span id="discount">00</span>%</span> (<em>Now <span id="currentPrice">0.00</span></em>)</span>
- </div>
</body>
</html>