Skip to content

Commit 5f1056b

Browse files
committed
fix: warm up AudioContext for raise hand chime, auto-end broadcast on disconnect, and guard server boot with INSTBYTE_BOOT flag
1 parent 09e6f83 commit 5f1056b

File tree

3 files changed

+37
-17
lines changed

3 files changed

+37
-17
lines changed

bin/instbyte.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,5 @@ process.env.INSTBYTE_UPLOADS = uploadsDir;
2525
// ========================
2626
// BOOT
2727
// ========================
28+
process.env.INSTBYTE_BOOT = '1';
2829
require("../server/server.js");

client/js/app.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ let broadcastInterval = null;
1515
let lastFrameData = null;
1616
let broadcastChannel = 'general';
1717
let viewerCount = 0;
18+
let audioCtx = null;
1819

1920
const seenObserver = new IntersectionObserver((entries) => {
2021
entries.forEach(entry => {
@@ -171,7 +172,7 @@ function escapeHtml(str) {
171172

172173
function playChime() {
173174
try {
174-
const ctx = new (window.AudioContext || window.webkitAudioContext)();
175+
const ctx = audioCtx || new (window.AudioContext || window.webkitAudioContext)();
175176

176177
const gain = ctx.createGain();
177178
gain.connect(ctx.destination);
@@ -1729,7 +1730,7 @@ async function startBroadcast() {
17291730
const startRes = await fetch('/broadcast/start', {
17301731
method: 'POST',
17311732
headers: { 'Content-Type': 'application/json' },
1732-
body: JSON.stringify({ uploader, channel: broadcastChannel })
1733+
body: JSON.stringify({ uploader, channel: broadcastChannel, socketId: socket.id })
17331734
});
17341735

17351736
if (!startRes.ok) {
@@ -1742,6 +1743,12 @@ async function startBroadcast() {
17421743
broadcastStream = stream;
17431744
isBroadcasting = true;
17441745

1746+
// Warm up AudioContext while we have a user gesture
1747+
try {
1748+
audioCtx = new (window.AudioContext || window.webkitAudioContext)();
1749+
await audioCtx.resume();
1750+
} catch (e) { }
1751+
17451752
// Set up canvas for frame capture
17461753
broadcastCanvas = document.createElement('canvas');
17471754
broadcastCtx = broadcastCanvas.getContext('2d');
@@ -1963,11 +1970,13 @@ socket.on('broadcast-reaction-received', ({ from }) => {
19631970
// Only show to broadcaster
19641971
if (!isBroadcasting) return;
19651972

1973+
playChime();
1974+
19661975
const toast = document.createElement('div');
19671976
toast.className = 'raise-hand-toast';
19681977
toast.textContent = `✋ ${from} raised their hand`;
19691978
document.body.appendChild(toast);
1970-
setTimeout(() => toast.remove(), 3000);
1979+
setTimeout(() => toast.remove(), 8000);
19711980
});
19721981

19731982
(async function init() {

server/server.js

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -833,14 +833,15 @@ app.get("/broadcast/status", (req, res) => {
833833

834834
/* POST /broadcast/start */
835835
app.post("/broadcast/start", broadcastLimiter, (req, res) => {
836+
836837
if (currentBroadcast) {
837838
return res.status(409).json({
838839
error: "Broadcast already in progress",
839840
uploader: currentBroadcast.uploader
840841
});
841842
}
842843

843-
const { uploader, channel } = req.body;
844+
const { uploader, channel, socketId } = req.body;
844845
if (!uploader || !channel) {
845846
return res.status(400).json({ error: "uploader and channel required" });
846847
}
@@ -849,7 +850,8 @@ app.post("/broadcast/start", broadcastLimiter, (req, res) => {
849850
uploader,
850851
channel,
851852
startedAt: Date.now(),
852-
lastFrame: null
853+
lastFrame: null,
854+
socketId
853855
};
854856

855857
io.emit("broadcast-started", {
@@ -978,6 +980,12 @@ io.on("connection", (socket) => {
978980
connectedUsers--;
979981
console.log(username + " disconnected | total:", connectedUsers);
980982
io.emit("user-count", connectedUsers);
983+
984+
if (currentBroadcast && currentBroadcast.socketId === socket.id) {
985+
currentBroadcast = null;
986+
io.emit("broadcast-ended");
987+
console.log("Broadcast ended — broadcaster disconnected");
988+
}
981989
});
982990
});
983991

@@ -1026,19 +1034,21 @@ const PREFERRED = parseInt(process.env.PORT) || config.server.port;
10261034
const localIP = getLocalIP();
10271035

10281036
let PORT;
1029-
findFreePort(PREFERRED).then(p => {
1030-
PORT = p;
1031-
server.listen(PORT, () => {
1032-
console.log("\nInstbyte running");
1033-
console.log("Local: http://localhost:" + PORT);
1034-
console.log("Network: http://" + localIP + ":" + PORT);
1035-
if (PORT !== PREFERRED) {
1036-
console.log(`(port ${PREFERRED} was busy, switched to ${PORT})`);
1037-
}
1038-
console.log("");
1039-
scanOrphans();
1037+
if (process.env.INSTBYTE_BOOT === '1') {
1038+
findFreePort(PREFERRED).then(p => {
1039+
PORT = p;
1040+
server.listen(PORT, () => {
1041+
console.log("\nInstbyte running");
1042+
console.log("Local: http://localhost:" + PORT);
1043+
console.log("Network: http://" + localIP + ":" + PORT);
1044+
if (PORT !== PREFERRED) {
1045+
console.log(`(port ${PREFERRED} was busy, switched to ${PORT})`);
1046+
}
1047+
console.log("");
1048+
scanOrphans();
1049+
});
10401050
});
1041-
});
1051+
}
10421052

10431053
module.exports = { app, server, sessions };
10441054

0 commit comments

Comments
 (0)