akborana4 commited on
Commit
4e4abd3
·
verified ·
1 Parent(s): 141780b

Update server/server.js

Browse files
Files changed (1) hide show
  1. server/server.js +154 -47
server/server.js CHANGED
@@ -1,66 +1,173 @@
1
- const path = require('path');
2
- const http = require('http');
3
- const express = require('express');
4
- const WebSocket = require('ws');
 
 
 
 
 
 
 
5
 
6
  const app = express();
7
- const PORT = process.env.PORT || 7860;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
 
9
- // Serve static frontend
10
- const staticPath = path.join(__dirname, '..', 'app');
11
- app.use(express.static(staticPath));
 
 
 
 
 
 
 
 
 
 
12
 
13
- // Fallback to index.html for SPA behavior
14
- app.get('*', (req, res) => {
15
- res.sendFile(path.join(staticPath, 'index.html'));
 
 
16
  });
17
 
18
  const server = http.createServer(app);
19
- const wss = new WebSocket.Server({ server });
 
 
20
 
21
- console.log(`WebSocket + HTTP server starting on port ${PORT}`);
 
22
 
23
- // rooms: { roomId: Set<WebSocket> }
24
- const rooms = {};
 
 
25
 
26
- wss.on('connection', (socket) => {
27
- console.log('New WS connection');
28
 
29
- socket.on('message', (raw) => {
30
- let data;
31
- try {
32
- data = JSON.parse(raw);
33
- } catch (e) {
34
- console.warn('Invalid JSON received:', raw);
35
- return;
36
- }
 
 
 
37
 
38
- if (data.type === 'join') {
39
- socket.roomId = data.room;
40
- rooms[data.room] = rooms[data.room] || new Set();
41
- rooms[data.room].add(socket);
42
- console.log(`Client joined room: ${data.room}`);
43
- }
44
 
45
- if (data.type === 'sync') {
46
- const roomSet = rooms[socket.roomId] || new Set();
47
- roomSet.forEach((client) => {
48
- if (client !== socket && client.readyState === WebSocket.OPEN) {
49
- client.send(JSON.stringify(data));
50
- }
51
- });
52
- }
53
  });
54
 
55
- socket.on('close', () => {
56
- if (socket.roomId && rooms[socket.roomId]) {
57
- rooms[socket.roomId].delete(socket);
58
- if (rooms[socket.roomId].size === 0) delete rooms[socket.roomId];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
  }
60
- console.log('WS connection closed');
61
  });
62
  });
63
 
64
- server.listen(PORT, '0.0.0.0', () => {
65
- console.log(`HTTP + WebSocket server listening on port ${PORT}`);
66
- });
 
 
1
+ import express from 'express';
2
+ import http from 'http';
3
+ import cors from 'cors';
4
+ import { Server as SocketIOServer } from 'socket.io';
5
+ import path from 'path';
6
+ import { fileURLToPath } from 'url';
7
+ import axios from 'axios';
8
+ import { searchSongs, resolveStreamUrl } from './jiosaavn.js';
9
+
10
+ const __filename = fileURLToPath(import.meta.url);
11
+ const __dirname = path.dirname(__filename);
12
 
13
  const app = express();
14
+ app.use(cors());
15
+ app.use(express.json());
16
+
17
+ // In-memory rooms: swap to Redis for production
18
+ const rooms = new Map();
19
+ /*
20
+ room = {
21
+ id,
22
+ hostId,
23
+ users: Set<socketId>,
24
+ track: { url, title, meta },
25
+ isPlaying: false,
26
+ // hostTime bookkeeping:
27
+ // 'anchor' is the playback position at 'anchorAt' (ms, Date.now())
28
+ anchor: 0,
29
+ anchorAt: 0
30
+ }
31
+ */
32
+
33
+ function ensureRoom(roomId) {
34
+ if (!rooms.has(roomId)) {
35
+ rooms.set(roomId, {
36
+ id: roomId,
37
+ hostId: null,
38
+ users: new Set(),
39
+ track: null,
40
+ isPlaying: false,
41
+ anchor: 0,
42
+ anchorAt: 0
43
+ });
44
+ }
45
+ return rooms.get(roomId);
46
+ }
47
 
48
+ // Search proxy to avoid CORS issues on client
49
+ app.get('/api/search', async (req, res) => {
50
+ try {
51
+ const q = req.query.q || '';
52
+ const results = await searchSongs(q);
53
+ res.json(results);
54
+ } catch (e) {
55
+ res.status(500).json({ error: e.message });
56
+ }
57
+ });
58
+
59
+ // Optional: direct URL validation/proxy header check
60
+ app.get('/api/ping', (req, res) => res.json({ ok: true }));
61
 
62
+ // Serve frontend build
63
+ const clientDir = path.resolve(__dirname, '../client/dist');
64
+ app.use(express.static(clientDir));
65
+ app.get('*', (_, res) => {
66
+ res.sendFile(path.join(clientDir, 'index.html'));
67
  });
68
 
69
  const server = http.createServer(app);
70
+ const io = new SocketIOServer(server, {
71
+ cors: { origin: '*', methods: ['GET', 'POST'] }
72
+ });
73
 
74
+ io.on('connection', (socket) => {
75
+ let joinedRoom = null;
76
 
77
+ socket.on('join_room', ({ roomId, name, asHost = false }, ack) => {
78
+ const room = ensureRoom(roomId);
79
+ room.users.add(socket.id);
80
+ if (asHost || !room.hostId) room.hostId = socket.id;
81
 
82
+ socket.join(roomId);
83
+ joinedRoom = roomId;
84
 
85
+ // Acknowledge with current state
86
+ ack?.({
87
+ roomId,
88
+ isHost: socket.id === room.hostId,
89
+ state: {
90
+ track: room.track,
91
+ isPlaying: room.isPlaying,
92
+ anchor: room.anchor,
93
+ anchorAt: room.anchorAt
94
+ }
95
+ });
96
 
97
+ socket.to(roomId).emit('system', { type: 'join', name, id: socket.id });
98
+ });
 
 
 
 
99
 
100
+ socket.on('chat_message', ({ roomId, name, text }) => {
101
+ socket.to(roomId).emit('chat_message', { name, text, at: Date.now() });
 
 
 
 
 
 
102
  });
103
 
104
+ // Host control events
105
+ socket.on('set_track', ({ roomId, track }) => {
106
+ const room = rooms.get(roomId);
107
+ if (!room || socket.id !== room.hostId) return;
108
+ room.track = track;
109
+ room.isPlaying = false;
110
+ room.anchor = 0;
111
+ room.anchorAt = Date.now();
112
+ io.to(roomId).emit('set_track', { track });
113
+ io.to(roomId).emit('pause', { anchor: 0, anchorAt: room.anchorAt });
114
+ });
115
+
116
+ socket.on('play', ({ roomId }) => {
117
+ const room = rooms.get(roomId);
118
+ if (!room || socket.id !== room.hostId) return;
119
+ // Start from current anchor
120
+ room.isPlaying = true;
121
+ room.anchorAt = Date.now(); // resume ticking
122
+ io.to(roomId).emit('play', { anchor: room.anchor, anchorAt: room.anchorAt });
123
+ });
124
+
125
+ socket.on('pause', ({ roomId }) => {
126
+ const room = rooms.get(roomId);
127
+ if (!room || socket.id !== room.hostId) return;
128
+ // Freeze anchor at current logical time
129
+ const elapsed = Math.max(0, Date.now() - room.anchorAt) / 1000;
130
+ if (room.isPlaying) room.anchor += elapsed;
131
+ room.isPlaying = false;
132
+ room.anchorAt = Date.now();
133
+ io.to(roomId).emit('pause', { anchor: room.anchor, anchorAt: room.anchorAt });
134
+ });
135
+
136
+ socket.on('seek', ({ roomId, to }) => {
137
+ const room = rooms.get(roomId);
138
+ if (!room || socket.id !== room.hostId) return;
139
+ room.anchor = to;
140
+ room.anchorAt = Date.now();
141
+ io.to(roomId).emit('seek', { anchor: room.anchor, anchorAt: room.anchorAt, isPlaying: room.isPlaying });
142
+ });
143
+
144
+ socket.on('sync_request', ({ roomId }, ack) => {
145
+ const room = rooms.get(roomId);
146
+ if (!room) return;
147
+ ack?.({
148
+ track: room.track,
149
+ isPlaying: room.isPlaying,
150
+ anchor: room.anchor,
151
+ anchorAt: room.anchorAt
152
+ });
153
+ });
154
+
155
+ socket.on('disconnect', () => {
156
+ if (!joinedRoom) return;
157
+ const room = rooms.get(joinedRoom);
158
+ if (!room) return;
159
+ room.users.delete(socket.id);
160
+ if (socket.id === room.hostId) {
161
+ // Promote next user as host if available
162
+ const [nextHost] = room.users;
163
+ room.hostId = nextHost || null;
164
+ io.to(joinedRoom).emit('host_changed', { hostId: room.hostId });
165
  }
166
+ if (room.users.size === 0) rooms.delete(joinedRoom);
167
  });
168
  });
169
 
170
+ const PORT = process.env.PORT || 3000;
171
+ server.listen(PORT, () => {
172
+ console.log(`Server on :${PORT}`);
173
+ });