refactor: use sse instead of ws for listener count
This commit is contained in:
@@ -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
|
||||
|
66
server.py
66
server.py
@@ -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")
|
||||
|
@@ -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();
|
||||
|
Reference in New Issue
Block a user