diff options
-rw-r--r-- | src/loop-play.js | 115 | ||||
-rw-r--r-- | todo.txt | 8 |
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. |