« get me outta code hell

Loop-play.js cleanup - http-music - Command-line music player + utils (not a server!)
about summary refs log tree commit diff
diff options
context:
space:
mode:
authorliam4 <towerofnix@gmail.com>2017-06-11 10:52:29 -0300
committerliam4 <towerofnix@gmail.com>2017-06-11 10:52:29 -0300
commitb1184c312d3963de5b324777f4a743be057535f1 (patch)
tree99140a3b46b9e89498dc59a916cbaa218e1f61fc
parenta48c6bbbbcca48ddc0bade66b33781e13f80213d (diff)
Loop-play.js cleanup
-rw-r--r--src/loop-play.js115
-rw-r--r--todo.txt8
2 files changed, 75 insertions, 48 deletions
diff --git a/src/loop-play.js b/src/loop-play.js
index ff77940..100c14b 100644
--- a/src/loop-play.js
+++ b/src/loop-play.js
@@ -1,29 +1,20 @@
 'use strict'
 
-const fs = require('fs')
-const tempy = require('tempy')
-
 const { spawn } = require('child_process')
-const { promisify } = require('util')
-const fetch = require('node-fetch')
-const path = require('path')
 const promisifyProcess = require('./promisify-process')
 const sanitize = require('sanitize-filename')
+const tempy = require('tempy')
 
-const writeFile = promisify(fs.writeFile)
-
-module.exports = function loopPlay(picker, downloader, playArgs = []) {
-  // Looping play function. Takes one argument, the "pick" function,
-  // which returns a track to play. Preemptively downloads the next
-  // track while the current one is playing for seamless continuation
-  // from one song to the next. Stops when the result of the pick
-  // function is null (or similar). Optionally takes a second argument
-  // used as arguments to the `play` process (before the file name).
+class DownloadController {
+  constructor(picker, downloader) {
+    this.process = null
 
-  let playProcess, convertProcess
+    this.picker = picker
+    this.downloader = downloader
+  }
 
-  async function downloadNext() {
-    const picked = picker()
+  async downloadNext() {
+    const picked = this.picker()
 
     if (picked == null) {
       return false
@@ -32,34 +23,56 @@ module.exports = function loopPlay(picker, downloader, playArgs = []) {
     const [ title, downloaderArg ] = picked
     console.log(`Downloading ${title}..\nDownloader arg: ${downloaderArg}`)
 
-    const downloadFile = await downloader(downloaderArg)
+    const from = await this.downloader(downloaderArg)
 
     const tempDir = tempy.directory()
-    const wavFile = tempDir + `/.${sanitize(title)}.wav`
+    const to = tempDir + `/.${sanitize(title)}.wav`
+
+    // We pass false to promisifyProcess to show we want hte output of avconv
+    // to be silenced.
+    const convertProcess = spawn('avconv', ['-y', '-i', from, to])
+    const convertPromise = promisifyProcess(convertProcess, false)
+
+    this.wavFile = to
+    this.process = convertProcess
 
     try {
-      const convertPromise = convert(downloadFile, wavFile)
-      convertProcess = convertPromise.process
       await convertPromise
     } catch(err) {
       console.warn("Failed to convert " + title)
       console.warn("Selecting a new track\n")
 
-      return await downloadNext()
+      this.killProcess()
+
+      return await this.downloadNext()
     }
+  }
 
-    return wavFile
+  killProcess() {
+    if (this.process) {
+      this.process.kill()
+    }
   }
+}
 
-  async function main() {
-    let wavFile = await downloadNext()
+class PlayController {
+  constructor(downloadController) {
+    this.playArgs = []
+    this.process = null
+
+    this.downloadController = downloadController
+  }
 
-    while (wavFile) {
-      const nextPromise = downloadNext()
+  async loopPlay() {
+    await this.downloadController.downloadNext()
 
-      // What a mouthful!
-      const playPromise = playFile(wavFile, playArgs)
-      playProcess = playPromise.process
+    while (this.downloadController.wavFile) {
+      const nextPromise = this.downloadController.downloadNext()
+
+      const file = this.downloadController.wavFile
+      const playProcess = spawn('play', [...this.playArgs, file])
+      const playPromise = promisifyProcess(playProcess)
+      this.process = playProcess
 
       try {
         await playPromise
@@ -67,32 +80,42 @@ module.exports = function loopPlay(picker, downloader, playArgs = []) {
         console.warn(err)
       }
 
-      wavFile = await nextPromise
+      await nextPromise
     }
   }
 
-  const promise = main()
+  killProcess() {
+    if (this.process) {
+      this.process.kill()
+    }
+  }
+}
+
+module.exports = function loopPlay(picker, downloader, playArgs = []) {
+  // Looping play function. Takes one argument, the "pick" function,
+  // which returns a track to play. Preemptively downloads the next
+  // track while the current one is playing for seamless continuation
+  // from one song to the next. Stops when the result of the pick
+  // function is null (or similar). Optionally takes a second argument
+  // used as arguments to the `play` process (before the file name).
+
+  const downloadController = new DownloadController(picker, downloader)
+
+  const playController = new PlayController(downloadController)
+  playController.playArgs = playArgs
+
+  const promise = playController.loopPlay()
 
   return {
     promise,
 
     skip: function() {
-      if (playProcess) playProcess.kill()
+      playController.killProcess()
     },
 
     kill: function() {
-      if (playProcess) playProcess.kill()
-      if (convertProcess) convertProcess.kill()
+      playController.killProcess()
+      downloadController.killProcess()
     }
   }
 }
-
-function convert(fromFile, toFile) {
-  const avconv = spawn('avconv', ['-y', '-i', fromFile, toFile])
-  return promisifyProcess(avconv, false)
-}
-
-function playFile(file, opts = []) {
-  const play = spawn('play', [...opts, file])
-  return Object.assign(promisifyProcess(play), {process: play})
-}
diff --git a/todo.txt b/todo.txt
index adf4456..a2526f0 100644
--- a/todo.txt
+++ b/todo.txt
@@ -102,8 +102,12 @@ TODO: Figure out a less "hacky" way to kill the process. Ideally we shouldn't
       broken, since we aren't using the shell's normal way of handling any
       keyboard controls such as those!
 
-TODO: A way to kill the up-next song.
-
 TODO: Separate the code in loop-play.js to be a bit nicer.
+      (Done!)
 
 TODO: Cleaning up http-music.js would be nice as well!
+
+TODO: A way to kill the up-next song.
+
+TODO: A way to see information about the currently playing song, as well as
+      the up-next song.