« get me outta code hell

basic command relay across socket clients - 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:
authorFlorrie <towerofnix@gmail.com>2020-07-10 20:52:13 -0300
committerFlorrie <towerofnix@gmail.com>2020-07-10 20:52:13 -0300
commitd00b26b23d9b3fc1e54a4d117366f0f22e664135 (patch)
tree7bc214a0ca10eec5ea6ae5d641481409aa00cc6d /socket.js
parent503a37ba4d7550f9c2ed1602e589a0142a20d10d (diff)
basic command relay across socket clients
Diffstat (limited to 'socket.js')
-rw-r--r--socket.js130
1 files changed, 113 insertions, 17 deletions
diff --git a/socket.js b/socket.js
index 5bfe80a..ae8ef87 100644
--- a/socket.js
+++ b/socket.js
@@ -48,8 +48,23 @@ function validateCommand(command) {
         case 'initialize-backend':
           return typeof command.backend === 'object'
       }
-      break
+      // No break here; servers can send commands which typically come from
+      // clients too.
     case 'client':
+      switch (command.code) {
+        case 'seek-to':
+          return (
+            typeof command.queuePlayer === 'string' &&
+            typeof command.time === 'number'
+          )
+        case 'set-pause':
+          return (
+            typeof command.queuePlayer === 'string' &&
+            typeof command.paused === 'boolean'
+          )
+        case 'status':
+          return typeof command.status === 'string'
+      }
       break
   }
 
@@ -70,7 +85,7 @@ function makeSocketServer() {
 
   server.canonicalBackend = null
 
-  function receivedData(data) {
+  function receivedData(socket, data) {
     // Parse data as a command and validate it. If invalid, drop this data.
 
     let command
@@ -80,14 +95,45 @@ function makeSocketServer() {
       return
     }
 
+    command.sender = 'client'
+
     if (!validateCommand(command)) {
       return
     }
 
+    // If it's a status command, respond appropriately, and return so that it
+    // is not relayed.
+
+    if (command.code === 'status') {
+      switch (command.status) {
+        case 'sync-playback':
+          for (const QP of server.canonicalBackend.queuePlayers) {
+            if (QP.timeData) {
+              socket.write(JSON.stringify({
+                sender: 'server',
+                code: 'seek-to',
+                queuePlayer: QP.id,
+                time: QP.timeData.curSecTotal
+              }) + '\n')
+              socket.write(JSON.stringify({
+                sender: 'server',
+                code: 'set-pause',
+                queuePlayer: QP.id,
+                paused: false
+              }))
+            }
+          }
+
+          break
+      }
+
+      return
+    }
+
     // Relay the data to client sockets.
 
     for (const socket of sockets) {
-      socket.write(command)
+      socket.write(JSON.stringify(command))
     }
   }
 
@@ -100,12 +146,20 @@ function makeSocketServer() {
       }
     })
 
-    socket.on('data', receivedData)
+    socket.on('data', data => receivedData(socket, data))
+
+    const savedBackend = saveBackend(server.canonicalBackend)
+
+    for (const qpData of savedBackend.queuePlayers) {
+      if (qpData.playerInfo) {
+        qpData.playerInfo.isPaused = true
+      }
+    }
 
     socket.write(JSON.stringify({
       sender: 'server',
       code: 'initialize-backend',
-      backend: saveBackend(server.canonicalBackend)
+      backend: savedBackend
     }))
   })
 
@@ -129,18 +183,20 @@ function makeSocketClient() {
     // Same sort of "guarding" deserialization/validation as in the server
     // code, because it's possible the client and server backends mismatch.
 
-    let command
-    try {
-      command = deserializeDataToCommand(data)
-    } catch (error) {
-      return
-    }
+    for (const line of data.toString().split('\n')) {
+      let command
+      try {
+        command = deserializeDataToCommand(line)
+      } catch (error) {
+        return
+      }
 
-    if (!validateCommand(command)) {
-      return
-    }
+      if (!validateCommand(command)) {
+        return
+      }
 
-    client.emit('command', command)
+      client.emit('command', command)
+    }
   })
 
   return client
@@ -160,11 +216,51 @@ function attachBackendToSocketClient(backend, client, {
             await restoreBackend(backend, command.backend)
             // TODO: does this need to be called here?
             updateRestoredTracksUsingPlaylists(backend, getPlaylistSources())
-            break
+            backend.on('playing', QP => {
+              QP.once('received time data', () => {
+                client.sendCommand({code: 'status', status: 'sync-playback'})
+              })
+            })
+            return
+        }
+        // Again, no pause. Client commands can come from the server.
+      case 'client': {
+        let QP = (
+          command.queuePlayer &&
+          backend.queuePlayers.find(QP => QP.id === command.queuePlayer)
+        )
+
+        switch (command.code) {
+          case 'seek-to':
+            if (QP) QP.seekTo(command.time)
+            return
+          case 'set-pause':
+            if (QP) QP.setPause(command.paused)
+            return
         }
-        break
+      }
     }
   })
+
+  backend.on('toggle-pause', queuePlayer => {
+    client.sendCommand({
+      code: 'set-pause',
+      queuePlayer: queuePlayer.id,
+      paused: queuePlayer.player.isPaused
+    })
+  })
+
+  function handleSeek(queuePlayer) {
+    client.sendCommand({
+      code: 'seek-to',
+      queuePlayer: queuePlayer.id,
+      time: queuePlayer.time
+    })
+  }
+
+  backend.on('seek-ahead', handleSeek)
+  backend.on('seek-back', handleSeek)
+  backend.on('seek-to', handleSeek)
 }
 
 function attachSocketServerToBackend(server, backend) {