2025-12-10 04:44:22 -05:00

81 lines
2.6 KiB
JavaScript

require('dotenv').config();
const express = require('express');
const http = require('http');
const { Server } = require("socket.io");
const axios = require('axios');
const app = express();
const server = http.createServer(app);
const io = new Server(server);
app.use(express.static('public'));
// --- Cloudflare Credentials ---
app.get('/api/get-turn-credentials', async (req, res) => {
try {
const response = await axios.post(
`https://rtc.live.cloudflare.com/v1/turn/keys/${process.env.CLOUDFLARE_APP_ID}/credentials/generate-ice-servers`,
{ ttl: 86400 },
{ headers: { 'Authorization': `Bearer ${process.env.CLOUDFLARE_API_TOKEN}`, 'Content-Type': 'application/json' } }
);
res.json(response.data);
} catch (error) {
console.error("Cloudflare error:", error.message);
res.status(500).json({ error: "Failed to fetch credentials" });
}
});
// --- Signaling Logic ---
let streamers = {}; // socketId -> { name: "Gaming Stream" }
// Add a map to track relationships: viewerSocketId -> streamerSocketId
let relationships = {};
io.on('connection', (socket) => {
// 1. Immediately send the list of active streams to the new user
socket.emit('streamer_list_update', streamers);
// 2. User wants to go live
socket.on('start_stream', (name) => {
streamers[socket.id] = { name: name };
io.emit('streamer_list_update', streamers); // Notify everyone
});
// 3. User wants to watch a specific streamer
socket.on('join_stream', (streamerId) => {
relationships[socket.id] = streamerId; // Record the link
io.to(streamerId).emit('viewer_joined', { viewerId: socket.id });
});
// 4. WebRTC Signaling (Offer/Answer/ICE)
socket.on('webrtc_offer', (data) => {
io.to(data.target).emit('webrtc_offer', { sdp: data.sdp, sender: socket.id });
});
socket.on('webrtc_answer', (data) => {
io.to(data.target).emit('webrtc_answer', { sdp: data.sdp, sender: socket.id });
});
socket.on('ice_candidate', (data) => {
io.to(data.target).emit('ice_candidate', { candidate: data.candidate, sender: socket.id });
});
// 5. Cleanup
socket.on('disconnect', () => {
// 1. If a STREAMER left (existing code)
if (streamers[socket.id]) {
delete streamers[socket.id];
io.emit('streamer_list_update', streamers);
}
// 2. If a VIEWER left (NEW CODE)
if (relationships[socket.id]) {
const streamerId = relationships[socket.id];
// Tell the specific streamer that this specific viewer is gone
io.to(streamerId).emit('viewer_left', { viewerId: socket.id });
delete relationships[socket.id];
}
});
});
const PORT = process.env.PORT || 3000;
server.listen(PORT, () => console.log(`Server running on http://localhost:${PORT}`));