diff --git a/src/classes/ClientConnection.ts b/src/classes/ClientConnection.ts index 95422d1..62445c9 100644 --- a/src/classes/ClientConnection.ts +++ b/src/classes/ClientConnection.ts @@ -13,19 +13,102 @@ export class ClientConnection { } async create(): Promise { - console.log('client', this.socket.id, 'connected'); this.socket.join(this.roomId); - this.emitReady(); + + const roomData = this.server.getRoomData(this.roomId); + const isHost = roomData?.hostId === this.socket.id; + + console.log('client', this.roomId, this.socket.id, roomData); + this.socket.emit('ready', { ...roomData, hostId: undefined, isHost }); + + this.handleEvents(); this.handleDisconnections(); } protected handleDisconnections() { - this.socket.on('disconnect', () => { - console.log('client', this.socket.id, 'disconnected'); + this.socket.on('disconnect', async () => { + console.log('client', this.roomId, this.socket.id, 'disconnected'); + const room = this.server.roomData.get(this.roomId); + + if (!room) return; + const roomRef = this.server.io.sockets.adapter.rooms.get(this.roomId); + const roomSize = roomRef?.size; + + if (!roomSize || roomSize === 0) { + this.server.roomData.delete(this.roomId); + return; + } + + if (room.hostId === this.socket.id) { + const socketIds = roomRef ? Array.from(roomRef) : []; + const newHostId = socketIds.find((id) => id !== this.socket.id) || null; + room.hostId = newHostId; + if (newHostId) { + this.socket.to(this.roomId).emit('newHost', { hostId: newHostId, isHost: false }); + this.server.io.to(newHostId).emit('newHost', { hostId: newHostId, isHost: true }); + } + } }); } - private emitReady() { - this.socket.emit('ready', {}); + private handleEvents() { + this.socket.on( + 'syncMedia', + (data: { + currentMedia: any; + currentMediaControls?: { + isPlaying: boolean; + isLooping: boolean; + }; + currentSeconds: number; + requesterId?: string; + }) => { + const room = this.server.roomData.get(this.roomId); + if (!room || room.hostId !== this.socket.id) return; + room.currentMedia = data.currentMedia; + room.currentMediaControls = data.currentMediaControls; + if (data.requesterId) { + this.server.io.to(data.requesterId).emit('syncMedia', data); + } else { + this.socket.broadcast.emit('syncMedia', data); + } + } + ); + + this.socket.on( + 'syncPlaylist', + (data: { + currentPlaylist: Record; + }) => { + const room = this.server.roomData.get(this.roomId); + if (!room || room.hostId !== this.socket.id) return; + room.currentPlaylist = data.currentPlaylist; + this.socket.broadcast.emit('syncPlaylist', data); + } + ); + + this.socket.on('addMediaToPlaylist', (media: any) => { + const room = this.server.roomData.get(this.roomId); + if (!room) return; + room.currentPlaylist[media.id] = media; + this.socket.broadcast.emit('syncPlaylist', { currentPlaylist: room.currentPlaylist }); + }); + + this.socket.on('seekTo', (currentSeconds: number) => { + const room = this.server.roomData.get(this.roomId); + if (!room || room.hostId !== this.socket.id) return; + this.socket.broadcast.emit('seekTo', currentSeconds); + }); + + this.socket.on( + 'requestSyncMedia', + () => { + const room = this.server.roomData.get(this.roomId); + if (!room || !room.hostId) return; + if (room.hostId === this.socket.id) return; + this.server.io.to(room.hostId).emit('requestSyncMedia', { requesterId: this.socket.id }); + } + ); + } } diff --git a/src/classes/SocketServer.ts b/src/classes/SocketServer.ts index aa5d8c6..af74e86 100644 --- a/src/classes/SocketServer.ts +++ b/src/classes/SocketServer.ts @@ -2,7 +2,19 @@ import { Server, Socket } from 'socket.io'; import { ClientConnection } from './ClientConnection'; export class SocketServer { - private readonly io: Server; + public readonly io: Server; + public readonly roomData = new Map< + string, + { + currentMedia: any; + currentPlaylist: any; + currentMediaControls?: { + isPlaying: boolean; + isLooping: boolean; + }; + hostId: string | null; + } + >(); public constructor() { const port = parseInt(process.env['PORT'] ?? '4001'); @@ -13,7 +25,7 @@ export class SocketServer { cors: { origin: [ process.env.NODE_ENV === 'development' - ? 'http://localhost:4000' + ? 'http://localhost:3000' : 'https://harmony-events.tnfangel.com' ], methods: ['GET', 'POST', 'PATCH', 'DELETE'], @@ -27,7 +39,7 @@ export class SocketServer { } public async setup() { - this.io.on('connection', this.createConnection); + this.io.on('connection', this.createConnection.bind(this)); } private async createConnection(socket: Socket) { @@ -38,6 +50,25 @@ export class SocketServer { return; } + if (!this.roomData.has(roomId)) { + this.roomData.set(roomId, { + hostId: socket.id, + currentMedia: null, + currentPlaylist: {}, + currentMediaControls: { + isLooping: false, + isPlaying: false + } + }); + } else { + const room = this.roomData.get(roomId); + if (room && !room.hostId) room.hostId = socket.id; + } + await new ClientConnection(this, socket, roomId).create(); } + + public getRoomData(roomId: string) { + return this.roomData.get(roomId) || null; + } }