danceinterpreter-rs 0.7.0

Danceinterpreter in Rust
const MAX_QUEUE = 20;
const DEBUG_LOGGING = false;

let sessionId = Bun.randomUUIDv7();
let baseTime = 0;
let state = null;
let queue = [];

let requiredImages = [];
let loadedImages = [];
let sentImages = [];

const resetConnection = () => {
	state = null;
	queue = [];

	requiredImages = [];
	loadedImages = [];
	sentImages = [];

	sessionId = Bun.randomUUIDv7();
};

const getNeededImages = () => requiredImages.filter(i => !loadedImages.includes(i));

const onUpdateState = () => {
	requiredImages = [
		state.deck0content.filePath,
		state.deck1content.filePath,
		state.deck2content.filePath,
		state.deck3content.filePath
	].filter(i => i);
	loadedImages = loadedImages.filter(i => requiredImages.includes(i));
	onChangeNeededImages();

	console.log(state);
};

const onChangeNeededImages = () => {
	const neededImages = getNeededImages();
	const newImages = neededImages.filter(i => !sentImages.includes(i));

	if (newImages.length > 0) console.log("needed images changed");

	for (const img of newImages) server.publish("cover", img);

	sentImages = neededImages;
};

const server = Bun.serve({
	port: 8080,
	routes: {
		"/connect": () =>
			new Response(
				JSON.stringify({
					sessionId,
					debugLogging: DEBUG_LOGGING
				})
			),
		"/init": {
			POST: async req => {
				const data = await req.json();

				if (data.sessionId === sessionId) {
					baseTime = data.timestamp;
					state = data.state;

					for (const update in queue) Object.apply(state, update);
					queue = [];

					onUpdateState();
				}

				return new Response(sessionId);
			}
		},
		"/update/:id": {
			POST: async req => {
				const data = await req.json();

				if (data.sessionId === sessionId) {
					if (!state) {
						queue.push(data.state);

						if (queue.length > MAX_QUEUE) resetConnection();
					} else {
						state[req.params.id] = data.state;
						onUpdateState();
					}
				}

				return new Response(sessionId);
			}
		},
		"/cover": {
			GET: async req => {
				if (server.upgrade(req)) return;

				return new Response("expected websocket connection", { status: 400 });
			},
			POST: async req => {
				const { searchParams } = URL.parse(req.url);
				const path = searchParams.get("path");
				if (!path) return new Response("'path' query param missing", { status: 400 });

				const data = await req.blob();
				loadedImages.push(path);

				console.log(`image "${path}" received`);

				const file = Bun.file(`./${Bun.randomUUIDv7()}.bin`);
				await Bun.write(file, data);
				return new Response("OK");
			}
		},
		"/log": {
			POST: async req => {
				if (req.body) console.log(`[log] ${await req.text()}`);
				return new Response("OK");
			}
		},
		"/*": async req => {
			console.log(`${req.method}\t${req.url}`);
			if (req.body) console.log(await req.text());

			return new Response("OK");
		}
	},

	async fetch(req) {
		console.log(`${req.method}\t${req.url}`);
		if (req.body) console.log(await req.text());

		return new Response("Not Found", { status: 404 });
	},

	websocket: {
		open(ws) {
			console.log("web socket connected");

			for (const img of sentImages) ws.send(img);

			ws.subscribe("cover");
		},
		close() {
			console.log("web socket disconnected");
		}
	}
});

console.log(`Listening on ${server.url}`);