Spaces:
Runtime error
Runtime error
Update server.js
Browse files
server.js
CHANGED
|
@@ -1247,6 +1247,17 @@ streamApp.post('/left/:streamId', async (req, res) => {
|
|
| 1247 |
res.json({ success: true, message: `User ${user.name} left stream ${streamId}`, totalUsers: stream.users.size });
|
| 1248 |
});
|
| 1249 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1250 |
// Viewer list
|
| 1251 |
streamApp.get('/list/:streamId', async (req, res) => {
|
| 1252 |
const rawId = req.params.streamId;
|
|
@@ -1274,8 +1285,9 @@ streamApp.post('/heartbeat/:streamId', async (req, res) => {
|
|
| 1274 |
activeViewers[userId] = streamId;
|
| 1275 |
}
|
| 1276 |
const user = stream.users.get(userId);
|
| 1277 |
-
if (user) { user.lastHeartbeat = new Date().toISOString();
|
| 1278 |
-
|
|
|
|
| 1279 |
});
|
| 1280 |
|
| 1281 |
// Current track info
|
|
@@ -1332,6 +1344,7 @@ streamApp.get('/api/streams', (req, res) => {
|
|
| 1332 |
currentSong: current ? { title: current.meta.title, thumbnail: current.meta.thumbnail, duration: current.meta.duration } : null,
|
| 1333 |
nextSong: next ? { title: next.meta.title, thumbnail: next.meta.thumbnail, duration: next.meta.duration } : null,
|
| 1334 |
activeListeners, queueLength: stream.queue.length, isPlaying: stream.isActive && current !== null,
|
|
|
|
| 1335 |
};
|
| 1336 |
});
|
| 1337 |
if (filter === 'active') activeStreams.sort((a, b) => b.activeListeners - a.activeListeners);
|
|
@@ -3089,12 +3102,16 @@ streamApp.post('/api/constituent/skip/:spaceName', requireRegistered, async (req
|
|
| 3089 |
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 3090 |
|
| 3091 |
streamApp.get('/api/stream-source/:streamId', async (req, res) => {
|
| 3092 |
-
const raw
|
| 3093 |
-
const streamId
|
|
|
|
| 3094 |
|
| 3095 |
// 1. Check constituents owned by this userId
|
| 3096 |
try {
|
| 3097 |
-
const
|
|
|
|
|
|
|
|
|
|
| 3098 |
for (const c of constituents) {
|
| 3099 |
try {
|
| 3100 |
const r = await axios.get(
|
|
|
|
| 1247 |
res.json({ success: true, message: `User ${user.name} left stream ${streamId}`, totalUsers: stream.users.size });
|
| 1248 |
});
|
| 1249 |
|
| 1250 |
+
// GET /listeners/:streamId β used by frontend watch page to show who's watching
|
| 1251 |
+
streamApp.get('/listeners/:streamId', async (req, res) => {
|
| 1252 |
+
const streamId = await resolveStreamId(req.params.streamId);
|
| 1253 |
+
const stream = streams[streamId];
|
| 1254 |
+
if (!stream) return res.json({ success: true, listeners: [] });
|
| 1255 |
+
const list = stream.users
|
| 1256 |
+
? Array.from(stream.users.values()).map(u => ({ name: u.name, isGuest: u.isGuest }))
|
| 1257 |
+
: [];
|
| 1258 |
+
res.json({ success: true, listeners: list });
|
| 1259 |
+
});
|
| 1260 |
+
|
| 1261 |
// Viewer list
|
| 1262 |
streamApp.get('/list/:streamId', async (req, res) => {
|
| 1263 |
const rawId = req.params.streamId;
|
|
|
|
| 1285 |
activeViewers[userId] = streamId;
|
| 1286 |
}
|
| 1287 |
const user = stream.users.get(userId);
|
| 1288 |
+
if (user) { user.lastHeartbeat = new Date().toISOString(); }
|
| 1289 |
+
// Return success regardless β constituent-stream viewers aren't in this map
|
| 1290 |
+
res.json({ success: true });
|
| 1291 |
});
|
| 1292 |
|
| 1293 |
// Current track info
|
|
|
|
| 1344 |
currentSong: current ? { title: current.meta.title, thumbnail: current.meta.thumbnail, duration: current.meta.duration } : null,
|
| 1345 |
nextSong: next ? { title: next.meta.title, thumbnail: next.meta.thumbnail, duration: next.meta.duration } : null,
|
| 1346 |
activeListeners, queueLength: stream.queue.length, isPlaying: stream.isActive && current !== null,
|
| 1347 |
+
hlsReady: !!(hlsState[streamId] && hlsState[streamId].segments.length > 0 && !hlsState[streamId].generating),
|
| 1348 |
};
|
| 1349 |
});
|
| 1350 |
if (filter === 'active') activeStreams.sort((a, b) => b.activeListeners - a.activeListeners);
|
|
|
|
| 3102 |
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 3103 |
|
| 3104 |
streamApp.get('/api/stream-source/:streamId', async (req, res) => {
|
| 3105 |
+
const raw = req.params.streamId;
|
| 3106 |
+
const streamId = await resolveStreamId(raw);
|
| 3107 |
+
const wantConstituent = req.query.constituentName || null; // optional filter
|
| 3108 |
|
| 3109 |
// 1. Check constituents owned by this userId
|
| 3110 |
try {
|
| 3111 |
+
const query = wantConstituent
|
| 3112 |
+
? { userId: streamId, spaceName: wantConstituent }
|
| 3113 |
+
: { userId: streamId };
|
| 3114 |
+
const constituents = await db.collection('constituents').find(query).toArray();
|
| 3115 |
for (const c of constituents) {
|
| 3116 |
try {
|
| 3117 |
const r = await axios.get(
|