refactor: use sse instead of ws for listener count

This commit is contained in:
2024-11-25 00:22:44 +00:00
parent 0ef8bc9e36
commit 087af66434
3 changed files with 81 additions and 80 deletions

View File

@@ -1,2 +1,4 @@
fastapi==0.111.1 fastapi==0.115.5
websocket_client==1.8.0 logger==1.4
Requests==2.32.3
sse_starlette==2.1.3

View File

@@ -1,14 +1,22 @@
import asyncio
import threading import threading
import os import os
import json
from time import sleep from time import sleep
import requests import requests
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
from fastapi import FastAPI, WebSocket, WebSocketDisconnect, status from fastapi import (
FastAPI,
Request,
HTTPException,
status,
)
from fastapi.responses import FileResponse from fastapi.responses import FileResponse
from fastapi.staticfiles import StaticFiles from fastapi.staticfiles import StaticFiles
from logger import log_info, log_warn from logger import log_info, log_warn
from websocket_connection_manager import WebSocketConnectionManager from websocket_connection_manager import WebSocketConnectionManager
from sse_starlette.sse import EventSourceResponse
# the index of the current audio track from 0 to 9 # the index of the current audio track from 0 to 9
current_index = -1 current_index = -1
@@ -130,34 +138,46 @@ def get_current_audio():
return FileResponse(f"{current_index}.mp3") return FileResponse(f"{current_index}.mp3")
@app.websocket("/ws") @app.get("/status")
async def ws_endpoint(ws: WebSocket): def status_stream(request: Request):
await ws_connection_manager.connect(ws) async def status_generator():
last_listener_count = len(active_listeners)
yield json.dumps({"listeners": last_listener_count})
addr = "" while True:
if ws.client: if await request.is_disconnected():
addr, _ = ws.client break
else:
await ws.close()
ws_connection_manager.disconnect(ws)
await ws_connection_manager.broadcast(f"{len(active_listeners)}") listener_count = len(active_listeners)
if listener_count != last_listener_count:
last_listener_count = listener_count
yield json.dumps({"listeners": listener_count})
await asyncio.sleep(1)
return EventSourceResponse(status_generator())
@app.post("/client-status")
async def change_status(request: Request):
body = await request.json()
try: try:
while True: is_listening = body["isListening"]
msg = await ws.receive_text()
if msg == "playing": client = request.client
active_listeners.add(addr) if not client:
await ws_connection_manager.broadcast(f"{len(active_listeners)}") raise HTTPException(status_code=400, detail="ip address unavailable.")
elif msg == "paused":
active_listeners.discard(addr)
await ws_connection_manager.broadcast(f"{len(active_listeners)}")
except WebSocketDisconnect: if is_listening:
active_listeners.discard(addr) active_listeners.add(client.host)
ws_connection_manager.disconnect(ws) else:
await ws_connection_manager.broadcast(f"{len(active_listeners)}") active_listeners.discard(client.host)
return {"isListening": is_listening}
except KeyError:
raise HTTPException(status_code=400, detail="'isListening' must be a boolean")
app.mount("/", StaticFiles(directory="web", html=True), name="web") app.mount("/", StaticFiles(directory="web", html=True), name="web")

View File

@@ -29,7 +29,6 @@ let maxVolume = 100;
let currentVolume = 0; let currentVolume = 0;
let saveVolumeTimeout = null; let saveVolumeTimeout = null;
let meowCount = 0; let meowCount = 0;
let ws = connectToWebSocket();
function playAudio() { function playAudio() {
// add a random query parameter at the end to prevent browser caching // add a random query parameter at the end to prevent browser caching
@@ -37,17 +36,13 @@ function playAudio() {
currentAudio.onplay = () => { currentAudio.onplay = () => {
isPlaying = true; isPlaying = true;
playBtn.innerText = "pause"; playBtn.innerText = "pause";
if (ws) { updateClientStatus({ isListening: true });
ws.send("playing");
}
}; };
currentAudio.onpause = () => { currentAudio.onpause = () => {
isPlaying = false; isPlaying = false;
currentVolume = 0; currentVolume = 0;
playBtn.innerText = "play"; playBtn.innerText = "play";
if (ws) { updateClientStatus({ isListening: false });
ws.send("paused");
}
}; };
currentAudio.onended = () => { currentAudio.onended = () => {
currentVolume = 0; currentVolume = 0;
@@ -107,18 +102,6 @@ function fadeOut() {
}, CROSSFADE_INTERVAL_MS); }, CROSSFADE_INTERVAL_MS);
} }
function animateCat() {
let current = 0;
setInterval(() => {
if (current === 3) {
current = 0;
} else {
current += 1;
}
catImg.src = `./images/cat-${current}.png`;
}, 500);
}
/** /**
* Allow audio to be played/paused using the space bar * Allow audio to be played/paused using the space bar
*/ */
@@ -143,33 +126,6 @@ function enableSpaceBarControl() {
}); });
} }
function connectToWebSocket() {
const ws = new WebSocket(
`${location.protocol === "https:" ? "wss:" : "ws:"}//${location.host}/ws`,
);
ws.onmessage = (event) => {
console.log(event.data);
if (typeof event.data !== "string") {
return;
}
const listenerCountStr = event.data;
const listenerCount = Number.parseInt(listenerCountStr);
if (Number.isNaN(listenerCount)) {
return;
}
if (listenerCount <= 1) {
listenerCountLabel.innerText = `${listenerCount} person tuned in`;
} else {
listenerCountLabel.innerText = `${listenerCount} ppl tuned in`;
}
};
return ws;
}
function changeVolume(volume) { function changeVolume(volume) {
maxVolume = volume; maxVolume = volume;
const v = maxVolume / 100; const v = maxVolume / 100;
@@ -229,6 +185,37 @@ function showNotification(title, content, duration) {
}, duration); }, duration);
} }
function listenToServerStatusEvent() {
const statusEvent = new EventSource("/status");
statusEvent.addEventListener("message", (event) => {
const data = JSON.parse(event.data);
updateListenerCountLabel(data.listeners);
});
}
function updateListenerCountLabel(newCount) {
if (newCount <= 1) {
listenerCountLabel.innerText = `${newCount} person tuned in`;
} else {
listenerCountLabel.innerText = `${newCount} ppl tuned in`;
}
}
async function updateClientStatus(status) {
await fetch("/client-status", {
method: "POST",
body: JSON.stringify({ isListening: status.isListening }),
headers: {
"Content-Type": "application/json",
},
keepalive: true,
});
}
window.addEventListener("beforeunload", (e) => {
updateClientStatus({ isListening: false });
});
playBtn.onmousedown = () => { playBtn.onmousedown = () => {
clickAudio.play(); clickAudio.play();
document.addEventListener( document.addEventListener(
@@ -288,15 +275,7 @@ meowAudio.onplay = () => {
// don't wanna jumpscare ppl // don't wanna jumpscare ppl
achievementUnlockedAudio.volume = 0.05; achievementUnlockedAudio.volume = 0.05;
window.addEventListener("offline", () => {
ws = null;
});
window.addEventListener("online", () => {
ws = connectToWebSocket();
});
loadMeowCount(); loadMeowCount();
loadInitialVolume(); loadInitialVolume();
animateCat();
enableSpaceBarControl(); enableSpaceBarControl();
listenToServerStatusEvent();