diff options
-rw-r--r-- | backend.js | 8 | ||||
-rw-r--r-- | socket.js | 61 | ||||
-rw-r--r-- | todo.txt | 2 | ||||
-rw-r--r-- | ui.js | 8 |
4 files changed, 54 insertions, 25 deletions
diff --git a/backend.js b/backend.js index 86da76e..817ce1d 100644 --- a/backend.js +++ b/backend.js @@ -889,15 +889,15 @@ export default class Backend extends EventEmitter { this.emit('log message', messageInfo) } - loadPartyGrouplike(partyGrouplike) { - this.emit('got party grouplike', partyGrouplike) + loadPartyGrouplike(socketId, partyGrouplike) { + this.emit('got party grouplike', socketId, partyGrouplike) } shareWithParty(item) { this.emit('share with party', item) } - partyGrouplikeUpdated(partyGrouplike) { - this.emit('party grouplike updated', partyGrouplike) + partyGrouplikeUpdated(socketId, partyGrouplike) { + this.emit('party grouplike updated', socketId, partyGrouplike) } } diff --git a/socket.js b/socket.js index 5f3ded0..42f4057 100644 --- a/socket.js +++ b/socket.js @@ -22,6 +22,8 @@ import EventEmitter from 'node:events' import * as net from 'node:net' +import shortid from 'shortid' + import { restoreBackend, restoreNewItem, @@ -102,6 +104,8 @@ function validateCommand(command) { switch (command.code) { case 'initialize-backend': return typeof command.backend === 'object' + case 'set-socket-id': + return typeof command.socketId === 'string' } // No break here; servers can send commands which typically come from // clients too. @@ -235,7 +239,7 @@ export function makeSocketServer() { // that's the responsibility of the caller (use server.listen()). const server = new net.Server() - const sockets = [] + const socketMap = Object.create(null) server.canonicalBackend = null @@ -244,13 +248,15 @@ export function makeSocketServer() { const donePlaying = {} server.on('connection', socket => { - sockets.push(socket) + const socketId = shortid.generate() + + socketMap[socketId] = socket let nickname = DEFAULT_NICKNAME socket.on('close', () => { - if (sockets.includes(socket)) { - sockets.splice(sockets.indexOf(socket), 1) + if (socketId in socketMap) { + delete socketMap[socketId] } }) @@ -265,6 +271,7 @@ export function makeSocketServer() { } command.sender = 'client' + command.senderSocketId = socketId command.senderNickname = nickname if (!validateCommand(command)) { @@ -278,25 +285,26 @@ export function makeSocketServer() { switch (command.status) { case 'done-playing': { const doneSockets = donePlaying[command.queuePlayer] - if (doneSockets && !doneSockets.includes(socket)) { - doneSockets.push(socket) - if (doneSockets.length === sockets.length) { + if (doneSockets && !doneSockets.includes(socketId)) { + doneSockets.push(socketId) + if (doneSockets.length === Object.keys(socketMap).length) { // determine next track - for (const socket of sockets) { + for (const socket of Object.values(socketMap)) { // play next track } delete donePlaying[command.queuePlayer] } } + break } case 'ready-to-resume': { const readySockets = readyToResume[command.queuePlayer] - if (readySockets && !readySockets.includes(socket)) { - readySockets.push(socket) - if (readySockets.length === sockets.length) { + if (readySockets && !readySockets.includes(socketId)) { + readySockets.push(socketId) + if (readySockets.length === Object.keys(socketMap).length) { const QP = server.canonicalBackend.queuePlayers.find(QP => QP.id === command.queuePlayer) silenceEvents(QP, ['set-pause'], () => QP.setPause(false)) - for (const socket of sockets) { + for (const socket of Object.values(socketMap)) { socket.write(serializeCommandToData({ sender: 'server', code: 'set-pause', @@ -351,7 +359,7 @@ export function makeSocketServer() { // Relay the command to client sockets besides the sender. - const otherSockets = sockets.filter(s => s !== socket) + const otherSockets = Object.values(socketMap).filter(s => s !== socket) for (const socket of otherSockets) { socket.write(serializeCommandToData(command) + '\n') @@ -368,6 +376,12 @@ export function makeSocketServer() { socket.write(serializeCommandToData({ sender: 'server', + code: 'set-socket-id', + socketId + }) + '\n') + + socket.write(serializeCommandToData({ + sender: 'server', code: 'initialize-backend', backend: savedBackend }) + '\n') @@ -384,6 +398,7 @@ export function makeSocketClient() { const client = new EventEmitter() client.socket = new net.Socket() client.nickname = DEFAULT_NICKNAME + client.socketId = null // Will be received from server. client.sendCommand = function(command) { const data = serializeCommandToData(command) @@ -424,11 +439,16 @@ export function attachBackendToSocketClient(backend, client, { // All actual logic for instances of the mtui backend interacting with each // other through commands lives here. - const partyGrouplike = {name: 'My Party Sources', isPartySources: true, items: []} + const partyGrouplike = { + name: `Party Sources - ${client.nickname}`, + isPartySources: true, + items: [] + } + + const partyGrouplikeMap = Object.create(null) backend.setAlwaysStartPaused(true) backend.setWaitWhenDonePlaying(true) - backend.loadPartyGrouplike(partyGrouplike) function logCommand(command) { const nickToMessage = nickname => `\x1b[32;1m${nickname}\x1b[0m` @@ -489,6 +509,8 @@ export function attachBackendToSocketClient(backend, client, { actionmsg = `updated their nickname (from ${nickToMessage(command.oldNickname)})` senderNickname = command.nickname break + case 'set-socket-id': + return case 'set-pause': if (command.paused) { actionmsg = 'paused the player' @@ -540,6 +562,11 @@ export function attachBackendToSocketClient(backend, client, { switch (command.sender) { case 'server': switch (command.code) { + case 'set-socket-id': + client.socketId = command.socketId + partyGrouplikeMap[command.socketId] = partyGrouplike + backend.loadPartyGrouplike(client.socketId, partyGrouplike) + return case 'initialize-backend': await restoreBackend(backend, command.backend) // TODO: does this need to be called here? @@ -551,7 +578,7 @@ export function attachBackendToSocketClient(backend, client, { }) return } - // Again, no pause. Client commands can come from the server. + // Again, no break. Client commands can come from the server. case 'client': { let QP = ( command.queuePlayer && @@ -767,7 +794,7 @@ export function attachBackendToSocketClient(backend, client, { if (partyGrouplike.items.every(x => x[originalSymbol] !== origItem)) { partyGrouplike.items.push(newItem) - backend.partyGrouplikeUpdated(partyGrouplike) + backend.partyGrouplikeUpdated(client.socketId, partyGrouplike) } }) } diff --git a/todo.txt b/todo.txt index f2a6562..07aabb8 100644 --- a/todo.txt +++ b/todo.txt @@ -693,6 +693,8 @@ TODO: Apparently pressing any key while the UI is booting up will make the TODO: Tabber tab list should be accessible via tab (key). +TODO: Show current index and number of tabs beside tabber tab list. + TODO: Pressing escape while you've got items selected should deselect those items, rather than stop playback! ...Or SHOULD IT??? Well, yes. But it's still handy to not be locked out of stopping playback altogether. diff --git a/ui.js b/ui.js index b9c417a..a83446c 100644 --- a/ui.js +++ b/ui.js @@ -587,11 +587,11 @@ export default class AppElement extends FocusElement { this.log.newLogMessage(messageInfo) } - handleGotPartyGrouplike(partyGrouplike) { - this.newPartyTab(partyGrouplike) + handleGotPartyGrouplike(socketId, partyGrouplike) { + this.newPartyTab(socketId, partyGrouplike) } - handlePartyGrouplikeUpdated(partyGrouplike) { + handlePartyGrouplikeUpdated(socketId, partyGrouplike) { for (const grouplikeListing of this.tabber.tabberElements) { if (grouplikeListing.grouplike === partyGrouplike) { grouplikeListing.loadGrouplike(partyGrouplike, false) @@ -1950,7 +1950,7 @@ export default class AppElement extends FocusElement { }) } - newPartyTab(partyGrouplike) { + newPartyTab(socketId, partyGrouplike) { const listing = this.newGrouplikeListing() listing.loadGrouplike(partyGrouplike) } |