Skip to content

Commit

Permalink
feat: add live listener count
Browse files Browse the repository at this point in the history
  • Loading branch information
kennethnym committed Jul 26, 2024
1 parent eae55ae commit 45853a4
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 17 deletions.
13 changes: 11 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
### Linux ###
*~

# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*
# temporary files which can be created if a process still has a handle open of a deleted file .fuse_hidden*

# KDE directory preferences
.directory
Expand Down Expand Up @@ -249,3 +248,13 @@ $RECYCLE.BIN/

# End of https://www.toptal.com/developers/gitignore/api/python,windows,macos,linux

0.mp3
1.mp3
2.mp3
3.mp3
4.mp3
5.mp3
6.mp3
7.mp3
8.mp3
9.mp3
37 changes: 35 additions & 2 deletions server.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@

import websocket
from contextlib import asynccontextmanager
from fastapi import FastAPI
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
from fastapi.responses import FileResponse
from fastapi.staticfiles import StaticFiles
from logger import log_info, log_warn
from websocket_connection_manager import WebSocketConnectionManager

# the index of the current audio track from 0 to 9
current_index = -1
Expand All @@ -15,6 +16,8 @@
# websocket connection to the inference server
ws = None
ws_url = ""
ws_connection_manager = WebSocketConnectionManager()
active_listeners = set()


@asynccontextmanager
Expand Down Expand Up @@ -83,7 +86,6 @@ def advance():
current_index = 0
else:
current_index = current_index + 1

threading.Thread(target=generate_new_audio).start()

t = threading.Timer(60, advance)
Expand All @@ -98,4 +100,35 @@ 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)

addr = ""
if ws.client:
addr, _ = ws.client
else:
await ws.close()
ws_connection_manager.disconnect(ws)

try:
while True:
msg = await ws.receive_text()

if msg == "playing":
active_listeners.add(addr)
await ws_connection_manager.broadcast(f"{len(active_listeners)}")
elif msg == "paused":
active_listeners.remove(addr)
await ws_connection_manager.broadcast(f"{len(active_listeners)}")

except WebSocketDisconnect:
if ws.client:
addr, _ = ws.client
active_listeners.discard(addr)
ws_connection_manager.disconnect(ws)

await ws_connection_manager.broadcast(f"{len(active_listeners)}")


app.mount("/", StaticFiles(directory="web", html=True), name="web")
11 changes: 8 additions & 3 deletions web/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,16 @@ <h2>infinite lo-fi music in the background</h2>
<div class="button-container">
<button id="play-btn" class="button">play</button>
</div>
<div class="volume-slider-container">
<output id="current-volume-label" for="volume-slider">100%</output>
<input id="volume-slider" type="range" min="0" max="100" step="1">

<div class="status-bar">
<p id="listener-count">0 person tuned in</p>
<div class="volume-slider-container">
<output id="current-volume-label" for="volume-slider">100%</output>
<input id="volume-slider" type="range" min="0" max="100" step="1" />
</div>
</div>
</main>

<img class="cat" src="./images/cat-0.png">
<img class="eeping-cat" src="./images/eeping-cat.png">
</div>
Expand Down
51 changes: 46 additions & 5 deletions web/script.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,40 @@
const CROSSFADE_DURATION_MS = 5000;
const CROSSFADE_INTERVAL_MS = 20;
const AUDIO_DURATION_MS = 60000;

const playBtn = document.getElementById("play-btn");
const catImg = document.getElementsByClassName("cat")[0];
const volumeSlider = document.getElementById("volume-slider");
const currentVolumeLabel = document.getElementById("current-volume-label");
const clickAudio = document.getElementById("click-audio");
const clickReleaseAudio = document.getElementById("click-release-audio");
const meowAudio = document.getElementById("meow-audio");

const CROSSFADE_DURATION_MS = 5000;
const CROSSFADE_INTERVAL_MS = 20;
const AUDIO_DURATION_MS = 60000;
const listenerCountLabel = document.getElementById("listener-count");

let isPlaying = false;
let isFading = false;
let currentAudio;
let maxVolume = 100;
let currentVolume = 0;
let ws = connectToWebSocket();

function playAudio() {
// add a random query parameter at the end to prevent browser caching
currentAudio = new Audio(`./current.mp3?t=${Date.now()}`);
currentAudio.onplay = () => {
isPlaying = true;
playBtn.innerText = "pause";
if (ws) {
ws.send("playing");
}
};
currentAudio.onpause = () => {
isPlaying = false;
currentVolume = 0;
playBtn.innerText = "play";
if (ws) {
ws.send("paused");
}
};
currentAudio.onended = () => {
currentVolume = 0;
Expand Down Expand Up @@ -122,6 +130,31 @@ function enableSpaceBarControl() {
});
}

function connectToWebSocket() {
const ws = new WebSocket(`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;
}

playBtn.onmousedown = () => {
clickAudio.play();
document.addEventListener(
Expand Down Expand Up @@ -157,7 +190,15 @@ volumeSlider.oninput = () => {
clickReleaseAudio.volume = volumeSlider.value / 100;
meowAudio.volume = volumeSlider.value / 100;
};

volumeSlider.value = 100;

window.addEventListener("offline", () => {
ws = null;
});

window.addEventListener("online", () => {
ws = connectToWebSocket();
});

animateCat();
enableSpaceBarControl();
24 changes: 19 additions & 5 deletions web/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,25 @@ a {
border-radius: 2px;
}

.status-bar {
position: absolute;
top: 0;
left: 0;
right: 0;
z-index: -10;
display: flex;
flex-direction: row;
justify-content: space-between;
padding: 2rem;
z-index: 0;
color: var(--text);
}

.status-bar > #listener-count {
margin: 0;
opacity: 0.8;
}

.header {
font-weight: 800;
margin-bottom: 1rem;
Expand Down Expand Up @@ -152,11 +171,6 @@ a {
}

.volume-slider-container {
position: absolute;
top: 0;
right: 0;
padding: 0.5rem 1rem;
margin: 1rem;
display: flex;
justify-content: start;
align-items: center;
Expand Down
19 changes: 19 additions & 0 deletions websocket_connection_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import asyncio
from fastapi import WebSocket


class WebSocketConnectionManager:
def __init__(self) -> None:
self.__active_connections: list[WebSocket] = []

async def connect(self, ws: WebSocket):
await ws.accept()
self.__active_connections.append(ws)

def disconnect(self, ws: WebSocket):
self.__active_connections.remove(ws)

async def broadcast(self, msg: str):
await asyncio.gather(
*[conn.send_text(msg) for conn in self.__active_connections]
)

0 comments on commit 45853a4

Please sign in to comment.