live / server.js
vsmdvic's picture
Upload 3 files
092fbff verified
const http = require("http");
const fs = require("fs");
const path = require("path");
const { WebSocketServer } = require("ws");
const PORT = process.env.PORT || 7860;
const httpServer = http.createServer((req, res) => {
let file = "index.html";
if (req.url === "/admin.html") file = "admin.html";
const filePath = path.join(__dirname, file);
fs.readFile(filePath, (err, data) => {
if (err) { res.writeHead(404); return res.end("Not found"); }
res.writeHead(200, { "Content-Type": "text/html" });
res.end(data);
});
});
const wss = new WebSocketServer({ server: httpServer });
let streamerWs = null;
let isLive = false;
const viewers = new Set();
function sendTo(ws, obj) {
if (ws && ws.readyState === 1) ws.send(JSON.stringify(obj));
}
wss.on("connection", (ws) => {
ws.role = null;
ws._vid = null;
ws.on("message", (raw) => {
let msg; try { msg = JSON.parse(raw); } catch { return; }
switch (msg.type) {
case "streamer_join":
ws.role = "streamer"; streamerWs = ws;
sendTo(ws, { type: "streamer_welcome", viewerCount: viewers.size });
break;
case "viewer_join":
ws.role = "viewer"; ws._vid = Math.random().toString(36).slice(2);
viewers.add(ws);
sendTo(ws, { type: "viewer_welcome", isLive, vid: ws._vid });
sendTo(streamerWs, { type: "viewer_count", count: viewers.size });
if (isLive) sendTo(streamerWs, { type: "new_viewer", vid: ws._vid });
break;
case "go_live":
if (ws.role !== "streamer") return;
isLive = true;
viewers.forEach(v => { sendTo(v, { type: "stream_started" }); });
viewers.forEach(v => sendTo(streamerWs, { type: "new_viewer", vid: v._vid }));
break;
case "end_live":
if (ws.role !== "streamer") return;
isLive = false;
viewers.forEach(v => sendTo(v, { type: "stream_ended" }));
break;
case "offer":
if (ws.role !== "streamer") return;
viewers.forEach(v => { if (v._vid === msg.vid) sendTo(v, { type: "offer", sdp: msg.sdp }); });
break;
case "answer":
sendTo(streamerWs, { type: "answer", sdp: msg.sdp, vid: ws._vid });
break;
case "candidate":
if (ws.role === "streamer") {
viewers.forEach(v => { if (v._vid === msg.vid) sendTo(v, { type: "candidate", candidate: msg.candidate }); });
} else {
sendTo(streamerWs, { type: "candidate", candidate: msg.candidate, vid: ws._vid });
}
break;
case "viewer_ready":
ws._vid = msg.vid || ws._vid;
sendTo(streamerWs, { type: "new_viewer", vid: ws._vid });
break;
}
});
ws.on("close", () => {
if (ws.role === "streamer") {
streamerWs = null; isLive = false;
viewers.forEach(v => sendTo(v, { type: "stream_ended" }));
} else if (ws.role === "viewer") {
viewers.delete(ws);
sendTo(streamerWs, { type: "viewer_count", count: viewers.size });
}
});
});
httpServer.listen(PORT, () => console.log(`Running on port ${PORT}`));