« get me outta code hell

mtui - Music Text User Interface - user-friendly command line music player
about summary refs log tree commit diff
path: root/socket.js
diff options
context:
space:
mode:
Diffstat (limited to 'socket.js')
-rw-r--r--socket.js131
1 files changed, 108 insertions, 23 deletions
diff --git a/socket.js b/socket.js
index b418853..a092e4a 100644
--- a/socket.js
+++ b/socket.js
@@ -10,6 +10,11 @@
 
 'use strict' // single quotes & no semicolons time babey
 
+// This is expected to be the same across both the client and the server.
+// There will probably be inconsistencies between sender clients and receiving
+// clients / the server otherwise.
+const DEFAULT_NICKNAME = '(Unnamed)'
+
 const EventEmitter = require('events')
 const net = require('net')
 
@@ -22,6 +27,7 @@ const {
 } = require('./serialized-backend')
 
 const {
+  getTimeStringsFromSec,
   silenceEvents
 } = require('./general-util')
 
@@ -96,7 +102,7 @@ function validateCommand(command) {
         case 'clear-queue-up-to':
           return (
             typeof command.queuePlayer === 'string' &&
-            isItemRef(command.topItem)
+            isItemRef(command.track)
           )
         case 'distribute-queue':
           return (
@@ -138,7 +144,8 @@ function validateCommand(command) {
           return (
             typeof command.queuePlayer === 'string' &&
             Array.isArray(command.tracks) &&
-            command.tracks.every(track => isItemRef(track))
+            command.tracks.every(track => isItemRef(track)) &&
+            ['shuffle'].includes(command.why)
           )
         case 'seek-to':
           return (
@@ -148,6 +155,7 @@ function validateCommand(command) {
         case 'set-nickname':
           return (
             typeof command.nickname === 'string' &&
+            typeof command.oldNickname === 'string' &&
             command.nickname.length >= 1 &&
             command.nickname.length <= 12
           )
@@ -225,7 +233,7 @@ function makeSocketServer() {
   server.on('connection', socket => {
     sockets.push(socket)
 
-    let nickname = '(Unnamed)'
+    let nickname = DEFAULT_NICKNAME
 
     socket.on('close', () => {
       if (sockets.includes(socket)) {
@@ -261,7 +269,7 @@ function makeSocketServer() {
               readySockets.push(socket)
               if (readySockets.length === sockets.length) {
                 for (const socket of sockets) {
-                  socket.write(JSON.stringify({
+                  socket.write(serializeCommandToData({
                     sender: 'server',
                     code: 'set-pause',
                     queuePlayer: command.queuePlayer,
@@ -277,13 +285,13 @@ function makeSocketServer() {
           case 'sync-playback':
             for (const QP of server.canonicalBackend.queuePlayers) {
               if (QP.timeData) {
-                socket.write(JSON.stringify({
+                socket.write(serializeCommandToData({
                   sender: 'server',
                   code: 'seek-to',
                   queuePlayer: QP.id,
                   time: QP.timeData.curSecTotal
                 }) + '\n')
-                socket.write(JSON.stringify({
+                socket.write(serializeCommandToData({
                   sender: 'server',
                   code: 'set-pause',
                   queuePlayer: QP.id,
@@ -304,8 +312,11 @@ function makeSocketServer() {
       }
 
       // If it's a 'set-nickname' command, save the nickname.
+      // Also attach the old nickname for display in log messages.
 
       if (command.code === 'set-nickname') {
+        command.oldNickname = nickname
+        command.senderNickname = nickname
         nickname = command.nickname
       }
 
@@ -314,7 +325,7 @@ function makeSocketServer() {
       const otherSockets = sockets.filter(s => s !== socket)
 
       for (const socket of otherSockets) {
-        socket.write(JSON.stringify(command) + '\n')
+        socket.write(serializeCommandToData(command) + '\n')
       }
     }))
 
@@ -326,7 +337,7 @@ function makeSocketServer() {
       }
     }
 
-    socket.write(JSON.stringify({
+    socket.write(serializeCommandToData({
       sender: 'server',
       code: 'initialize-backend',
       backend: savedBackend
@@ -343,6 +354,7 @@ function makeSocketClient() {
 
   const client = new EventEmitter()
   client.socket = new net.Socket()
+  client.nickname = DEFAULT_NICKNAME
 
   client.sendCommand = function(command) {
     const data = serializeCommandToData(command)
@@ -351,7 +363,9 @@ function makeSocketClient() {
   }
 
   client.setNickname = function(nickname) {
-    client.sendCommand({code: 'set-nickname', nickname})
+    let oldNickname = client.nickname
+    client.nickname = nickname
+    client.sendCommand({code: 'set-nickname', nickname, oldNickname})
   }
 
   client.socket.on('data', perLine(line => {
@@ -384,11 +398,78 @@ function attachBackendToSocketClient(backend, client, {
   backend.setAlwaysStartPaused(true)
 
   function logCommand(command) {
-    const nickname = command.sender === 'server' ? 'the server' : command.nickname
-    backend.showLogMessage(`${nickname} sent ${command.code}!`)
+    const nickToMessage = nickname => `\x1b[32;1m${nickname}\x1b[0m`
+    const itemToMessage = item => `\x1b[32m"${item.name}"\x1b[0m`
+    let fullmsg = '' // may be overridden
+    let actionmsg = `sent ${command.code}` // fallback
+    switch (command.code) {
+      case 'clear-queue':
+        actionmsg = 'cleared the queue'
+        break
+      case 'clear-queue-past':
+        actionmsg = `cleared the queue past ${itemToMessage(command.track)}`
+        break
+      case 'clear-queue-up-to':
+        actionmsg = `cleared the queue up to ${itemToMessage(command.track)}`
+        break
+      case 'distribute-queue':
+        actionmsg = `distributed ${itemToMessage(command.topItem)} across the queue ${command.opts.how}`
+        break
+      case 'initialize-backend':
+        return
+      case 'play':
+        actionmsg = `started playing ${itemToMessage(command.track)}`
+        break
+      case 'queue': {
+        let afterMessage = ''
+        if (isItemRef(command.afterItem)) {
+          afterMessage = ` after ${itemToMessage(command.afterItem)}`
+        } else if (command.afterItem === 'FRONT') {
+          afterMessage = ` at the front of the queue`
+        }
+        actionmsg = `queued ${itemToMessage(command.topItem)}` + afterMessage
+        break
+      }
+      case 'restore-queue':
+        if (command.why === 'shuffle') {
+          actionmsg = 'shuffled the queue'
+        }
+        break
+      case 'seek-to':
+        // TODO: the second value here should be the duration of the track
+        // (this will make values like 0:0x:yy / 1:xx:yy appear correctly)
+        actionmsg = `seeked to ${getTimeStringsFromSec(command.time, command.time).timeDone}`
+        break
+      case 'set-nickname':
+        fullmsg = `${nickToMessage(command.nickname)} updated their nickname (from ${nickToMessage(command.oldNickname)})`
+        break
+      case 'set-pause':
+        if (command.paused) {
+          actionmsg = 'paused the player'
+        } else {
+          actionmsg = 'resumed the player'
+        }
+        break
+      case 'stop-playing':
+        actionmsg = 'stopped the player'
+        break
+      case 'unqueue':
+        actionmsg = `removed ${itemToMessage(command.topItem)} from the queue`
+        break
+      case 'status':
+        return
+    }
+    if (!fullmsg) {
+      const nickname = command.sender === 'server' ? 'the server' : command.senderNickname
+      fullmsg = `${nickToMessage(nickname)} ${actionmsg}`
+    }
+    backend.showLogMessage(fullmsg)
   }
 
-  client.on('sent-command', logCommand)
+  client.on('sent-command', command => {
+    command.senderNickname = client.nickname
+    logCommand(command)
+  })
 
   client.on('command', async command => {
     logCommand(command)
@@ -419,17 +500,17 @@ function attachBackendToSocketClient(backend, client, {
             return
           case 'clear-queue-past':
             if (QP) silenceEvents(QP, ['clear-queue-past'], () => QP.clearQueuePast(
-              restoreNewItem(command.topItem, getPlaylistSources())
+              restoreNewItem(command.track, getPlaylistSources())
             ))
             return
           case 'clear-queue-up-to':
             if (QP) silenceEvents(QP, ['clear-queue-up-to'], () => QP.clearQueueUpTo(
-              restoreNewItem(command.topItem, getPlaylistSources())
+              restoreNewItem(command.track, getPlaylistSources())
             ))
             return
           case 'distribute-queue':
             if (QP) silenceEvents(QP, ['distribute-queue'], () => QP.distributeQueue(
-              restoreNewItem(command.topItem, getPlaylistSources()),
+              restoreNewItem(command.topItem),
               {
                 how: command.opts.how,
                 rangeEnd: command.opts.rangeEnd
@@ -452,7 +533,7 @@ function attachBackendToSocketClient(backend, client, {
             return
           case 'queue':
             if (QP) silenceEvents(QP, ['queue'], () => QP.queue(
-              restoreNewItem(command.topItem, getPlaylistSources()),
+              restoreNewItem(command.topItem),
               isItemRef(command.afterItem) ? restoreNewItem(command.afterItem, getPlaylistSources()) : command.afterItem,
               {
                 movePlayingTrack: command.opts.movePlayingTrack
@@ -461,14 +542,17 @@ function attachBackendToSocketClient(backend, client, {
             return
           case 'restore-queue':
             if (QP) {
-              QP.queueGrouplike.items = command.tracks.map(refData => restoreNewItem(refData))
-              // TODO: target just the one queue player. hacks = illegal
-              updateRestoredTracksUsingPlaylists(backend, getPlaylistSources())
+              QP.replaceAllItems(command.tracks.map(
+                refData => restoreNewItem(refData, getPlaylistSources())
+              ))
             }
+            return
           case 'seek-to':
             if (QP) silenceEvents(QP, ['seek-to'], () => QP.seekTo(command.time))
             return
           case 'set-pause': {
+            // TODO: there's an event leak here when toggling pause while
+            // nothing is playing
             let playingThisTrack = true
             QP.once('playing new track', () => {
               playingThisTrack = false
@@ -500,19 +584,19 @@ function attachBackendToSocketClient(backend, client, {
     })
   })
 
-  backend.on('clear-queue-past', (queuePlayer, topItem) => {
+  backend.on('clear-queue-past', (queuePlayer, track) => {
     client.sendCommand({
       code: 'clear-queue-past',
       queuePlayer: queuePlayer.id,
-      topItem: saveItemReference(topItem)
+      track: saveItemReference(track)
     })
   })
 
-  backend.on('clear-queue-up-to', (queuePlayer, topItem) => {
+  backend.on('clear-queue-up-to', (queuePlayer, track) => {
     client.sendCommand({
       code: 'clear-queue-up-to',
       queuePlayer: queuePlayer.id,
-      topItem: saveItemReference(topItem)
+      track: saveItemReference(track)
     })
   })
 
@@ -572,6 +656,7 @@ function attachBackendToSocketClient(backend, client, {
   backend.on('shuffle-queue', queuePlayer => {
     client.sendCommand({
       code: 'restore-queue',
+      why: 'shuffle',
       queuePlayer: queuePlayer.id,
       tracks: queuePlayer.queueGrouplike.items.map(saveItemReference)
     })