« get me outta code hell

Merge branch 'master' of https://github.com/towerofnix/http-music - http-music - Command-line music player + utils (not a server!)
about summary refs log tree commit diff
path: root/src/loop-play.js
diff options
context:
space:
mode:
authorFlorrie <towerofnix@gmail.com>2018-01-17 18:33:52 -0400
committerFlorrie <towerofnix@gmail.com>2018-01-17 18:33:52 -0400
commita701fcf0d35f667b5ed32cfaed88db3d2872a9cc (patch)
treeaa2ae3d2e29b237f01a1a1936919be10d0b8e831 /src/loop-play.js
parentfb2f9ecda0531a950a871c6b85d42e913f3874f7 (diff)
parent7831b28be25aae1e890ee1f4d3bd6969023c10da (diff)
Merge branch 'master' of https://github.com/towerofnix/http-music
Diffstat (limited to 'src/loop-play.js')
-rw-r--r--src/loop-play.js105
1 files changed, 80 insertions, 25 deletions
diff --git a/src/loop-play.js b/src/loop-play.js
index 34ac4d5..0fd94e4 100644
--- a/src/loop-play.js
+++ b/src/loop-play.js
@@ -11,15 +11,19 @@
 const { spawn } = require('child_process')
 const FIFO = require('fifo-js')
 const EventEmitter = require('events')
+const fs = require('fs')
+const util = require('util')
 const killProcess = require('./kill-process')
 const { HistoryController, generalPicker } = require('./pickers')
 
+const writeFile = util.promisify(fs.writeFile)
+
 const {
   getDownloaderFor, byName: downloadersByName, makeConverter
 } = require('./downloaders')
 
 const {
-  getItemPathString, safeUnlink, parentSymbol
+  getItemPathString, safeUnlink, parentSymbol, sourceSymbol
 } = require('./playlist-utils')
 
 function createStatusLine({percentStr, curStr, lenStr}) {
@@ -33,13 +37,34 @@ class Player extends EventEmitter {
     this.disablePlaybackStatus = false
   }
 
+  set process(newProcess) {
+    this._process = newProcess
+    this._process.on('exit', code => {
+      if (code !== 0 && !this._killed) {
+        this.emit('crashed', code)
+      }
+
+      this._killed = false
+    })
+  }
+
+  get process() {
+    return this._process
+  }
+
   playFile(file) {}
   seekAhead(secs) {}
   seekBack(secs) {}
   volUp(amount) {}
   volDown(amount) {}
   togglePause() {}
-  kill() {}
+
+  async kill() {
+    if (this.process) {
+      this._killed = true
+      await killProcess(this.process)
+    }
+  }
 
   printStatusLine(str) {
     // Quick sanity check - we don't want to print the status line if it's
@@ -107,12 +132,6 @@ class MPVPlayer extends Player {
       this.process.once('close', resolve)
     })
   }
-
-  async kill() {
-    if (this.process) {
-      await killProcess(this.process)
-    }
-  }
 }
 
 class ControllableMPVPlayer extends MPVPlayer {
@@ -228,9 +247,6 @@ class SoXPlayer extends Player {
   }
 
   async kill() {
-    if (this.process) {
-      await killProcess(this.process)
-    }
   }
 }
 
@@ -294,7 +310,7 @@ class DownloadController extends EventEmitter {
     try {
       downloadFile = await downloader(downloaderArg)
     } catch(err) {
-      this.emit('errored', err)
+      this.emit('errored', 'Download error: ' + err)
       return
     }
 
@@ -310,7 +326,7 @@ class DownloadController extends EventEmitter {
     try {
       convertFile = await this.converter(converterOptions)(downloadFile)
     } catch(err) {
-      this.emit('errored', err)
+      this.emit('errored', 'Convert error: ' + err)
       return
     } finally {
       // Whether the convertion succeeds or not (hence 'finally'), we should
@@ -345,14 +361,19 @@ class DownloadController extends EventEmitter {
 }
 
 class PlayController extends EventEmitter {
-  constructor(player, playlist, historyController, downloadController) {
+  constructor({
+    player, playlist, historyController, downloadController,
+    useConverterOptions = true,
+    trackDisplayFile = null // File to output current track path to.
+  }) {
     super()
 
     this.player = player
     this.playlist = playlist
     this.historyController = historyController
     this.downloadController = downloadController
-    this.useConverterOptions = true
+    this.useConverterOptions = useConverterOptions
+    this.trackDisplayFile = trackDisplayFile
 
     this.currentTrack = null
     this.nextTrack = null
@@ -360,6 +381,28 @@ class PlayController extends EventEmitter {
     this.stopped = false
     this.shouldMoveNext = true
     this.failedCount = 0
+    this.playFailCount = 0
+
+    this.player.on('crashed', () => {
+      if (this.currentTrack) {
+        console.log('\x1b[31mFailed to play track \x1b[1m' +
+          getItemPathString(this.currentTrack) + '\x1b[0m'
+        )
+      } else {
+        console.log('\x1b[31mFailed to play track.\x1b[0m')
+      }
+      this.playFailCount++
+
+      if (this.playFailCount >= 5) {
+        console.error(
+          '\x1b[31mFailed to play 5 tracks. Halting, to prevent damage to ' +
+          'the computer.\x1b[0m'
+        )
+
+        process.exit(1)
+        throw new Error('Intentionally halted - failed to play tracks.')
+      }
+    })
 
     this.player.on('printStatusLine', playerString => {
       let fullStatusLine = ''
@@ -440,6 +483,12 @@ class PlayController extends EventEmitter {
       ])
 
       if (next) {
+        if (this.trackDisplayFile) {
+          await writeFile(this.trackDisplayFile,
+            getItemPathString(this.currentTrack[sourceSymbol])
+          )
+        }
+
         await this.playFile(next)
 
         // Now that we're done playing the file, we should delete it.. unless
@@ -529,8 +578,8 @@ class PlayController extends EventEmitter {
             "prevent damage to the computer.\x1b[0m"
           )
 
-          process.exit(0)
-          throw new Error('Intentionally halted.')
+          process.exit(1)
+          throw new Error('Intentionally halted - failed to download tracks.')
         }
 
         // A little bit blecht, but.. this works.
@@ -603,7 +652,11 @@ class PlayController extends EventEmitter {
     this.stopped = true
   }
 
-  logTrackInfo() {
+  logTrackInfo(upNextTrackCount = 3, previousTrackCount = undefined) {
+    if (typeof previousTrackCount === 'undefined') {
+      previousTrackCount = upNextTrackCount
+    }
+
     const getColorMessage = t => {
       if (!t) return '\x1b[2m(No track)\x1b[0m'
 
@@ -626,13 +679,13 @@ class PlayController extends EventEmitter {
     const tl = hc.timeline
     const tlI = hc.timelineIndex
 
-    for (let i = Math.max(0, tlI - 2); i < tlI; i++) {
+    for (let i = Math.max(0, tlI - (previousTrackCount - 1)); i < tlI; i++) {
       console.log(`\x1b[2m(Prev) ${getCleanMessage(tl[i])}\x1b[0m`)
     }
 
     console.log(`\x1b[1m(Curr) \x1b[1m${getColorMessage(tl[tlI])}\x1b[0m`)
 
-    for (let i = tlI + 1; i < Math.min(tlI + 3, tl.length); i++) {
+    for (let i = tlI + 1; i < Math.min(tlI + upNextTrackCount, tl.length); i++) {
       console.log(`(Next) ${getCleanMessage(tl[i])}`)
     }
   }
@@ -643,7 +696,8 @@ module.exports = async function startLoopPlay(
     pickerOptions, playerCommand, converterCommand,
     useConverterOptions = true,
     disablePlaybackStatus = false,
-    startTrack = null
+    startTrack = null,
+    trackDisplayFile = null
   }
 ) {
   // Looping play function. Takes a playlist and an object containing general
@@ -686,11 +740,12 @@ module.exports = async function startLoopPlay(
     historyController.timeline.push(startTrack)
   }
 
-  const playController = new PlayController(
-    player, playlist, historyController, downloadController
-  )
+  const playController = new PlayController({
+    player, playlist, historyController, downloadController,
+    trackDisplayFile
+  })
 
-  Object.assign(playController, {playerCommand, useConverterOptions})
+  Object.assign(playController, {useConverterOptions})
 
   const promise = playController.loopPlay()