File size: 3,090 Bytes
0c7fd3a
 
 
 
 
 
 
 
092fbff
 
 
0c7fd3a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
092fbff
 
 
0c7fd3a
 
 
 
092fbff
 
 
0c7fd3a
 
 
 
 
 
092fbff
0c7fd3a
 
 
 
 
 
092fbff
0c7fd3a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
092fbff
 
 
0c7fd3a
 
 
 
 
 
 
092fbff
0c7fd3a
092fbff
 
0c7fd3a
 
 
 
092fbff
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
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}`));