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
websocket_client==1.8.0
fastapi==0.115.5
logger==1.4
Requests==2.32.3
sse_starlette==2.1.3

View File

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

View File

@@ -29,7 +29,6 @@ let maxVolume = 100;
let currentVolume = 0;
let saveVolumeTimeout = null;
let meowCount = 0;
let ws = connectToWebSocket();
function playAudio() {
// add a random query parameter at the end to prevent browser caching
@@ -37,17 +36,13 @@ function playAudio() {
currentAudio.onplay = () => {
isPlaying = true;
playBtn.innerText = "pause";
if (ws) {
ws.send("playing");
}
updateClientStatus({ isListening: true });
};
currentAudio.onpause = () => {
isPlaying = false;
currentVolume = 0;
playBtn.innerText = "play";
if (ws) {
ws.send("paused");
}
updateClientStatus({ isListening: false });
};
currentAudio.onended = () => {
currentVolume = 0;
@@ -107,18 +102,6 @@ function fadeOut() {
}, 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
*/
@@ -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) {
maxVolume = volume;
const v = maxVolume / 100;
@@ -229,6 +185,37 @@ function showNotification(title, content, 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 = () => {
clickAudio.play();
document.addEventListener(
@@ -288,15 +275,7 @@ meowAudio.onplay = () => {
// don't wanna jumpscare ppl
achievementUnlockedAudio.volume = 0.05;
window.addEventListener("offline", () => {
ws = null;
});
window.addEventListener("online", () => {
ws = connectToWebSocket();
});
loadMeowCount();
loadInitialVolume();
animateCat();
enableSpaceBarControl();
listenToServerStatusEvent();