diff options
Diffstat (limited to 'players.js')
-rw-r--r-- | players.js | 206 |
1 files changed, 49 insertions, 157 deletions
diff --git a/players.js b/players.js index be5205f..9941ce5 100644 --- a/players.js +++ b/players.js @@ -1,9 +1,4 @@ -// stolen from http-music - -const { spawn } = require('child_process') -const FIFO = require('fifo-js') const EventEmitter = require('events') -const { commandExists, killProcess } = require('./general-util') function getTimeStrings({curHour, curMin, curSec, lenHour, lenMin, lenSec}) { // Multiplication casts to numbers; addition prioritizes strings. @@ -51,21 +46,6 @@ 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) {} @@ -73,12 +53,7 @@ class Player extends EventEmitter { volDown(amount) {} togglePause() {} - async kill() { - if (this.process) { - this._killed = true - await killProcess(this.process) - } - } + async kill() {} printStatusLine(data) { // Quick sanity check - we don't want to print the status line if it's @@ -90,160 +65,77 @@ class Player extends EventEmitter { } } -module.exports.MPVPlayer = class extends Player { - getMPVOptions(file) { - return ['--no-video', file] - } - - playFile(file) { - // The more powerful MPV player. MPV is virtually impossible for a human - // being to install; if you're having trouble with it, try the SoX player. - - this.process = spawn('mpv', this.getMPVOptions(file)) - - this.process.stderr.on('data', data => { - if (this.disablePlaybackStatus) { - return - } - - const match = data.toString().match( - /(..):(..):(..) \/ (..):(..):(..) \(([0-9]+)%\)/ - ) - - if (match) { - const [ - curHour, curMin, curSec, // ##:##:## - lenHour, lenMin, lenSec, // ##:##:## - percent // ###% - ] = match.slice(1) +module.exports.WebPlayer = class extends Player { + constructor() { + super() - this.printStatusLine(getTimeStrings({curHour, curMin, curSec, lenHour, lenMin, lenSec})) - } + const secToMore = time => ({ + hour: Math.floor(time / 3600), + min: Math.floor((time % 3600) / 60), + sec: Math.floor(time % 60) }) - return new Promise(resolve => { - this.process.once('close', resolve) - }) - } -} + setInterval(() => { + if (!this.audioEl) return + + const { hour: curHour, min: curMin, sec: curSec } = secToMore(this.audioEl.currentTime) + const { hour: lenHour, min: lenMin, sec: lenSec } = secToMore(this.audioEl.duration) -module.exports.ControllableMPVPlayer = class extends module.exports.MPVPlayer { - getMPVOptions(file) { - return ['--input-file=' + this.fifo.path, ...super.getMPVOptions(file)] + this.printStatusLine(getTimeStrings({ + curHour, curMin, curSec, + lenHour, lenMin, lenSec + })) + }, 50) } playFile(file) { - this.fifo = new FIFO() - - return super.playFile(file) - } + this.audioEl = document.createElement('audio') + this.audioEl.src = file + this.audioEl.play() + + return Promise.race([ + new Promise(resolve => this.stopPromise = resolve), + new Promise(resolve => { + const handleEnded = () => { + this.audioEl.removeEventListener('ended', handleEnded) + resolve() + } - sendCommand(command) { - if (this.fifo) { - this.fifo.write(command) - } + this.audioEl.addEventListener('ended', handleEnded) + }) + ]) } seekAhead(secs) { - this.sendCommand(`seek +${parseFloat(secs)}`) + if (!this.audioEl) return + this.audioEl.currentTime += secs } seekBack(secs) { - this.sendCommand(`seek -${parseFloat(secs)}`) - } - - volUp(amount) { - this.sendCommand(`add volume +${parseFloat(amount)}`) - } - - volDown(amount) { - this.sendCommand(`add volume -${parseFloat(amount)}`) + if (!this.audioEl) return + this.audioEl.currentTime -= secs } togglePause() { - this.sendCommand('cycle pause') + if (!this.audioEl) return + if (this.audioEl.paused) { + this.audioEl.play() + } else { + this.audioEl.pause() + } } kill() { - if (this.fifo) { - this.fifo.close() - delete this.fifo + if (!this.audioEl) return + this.audioEl.currentTime = 0 + this.audioEl.pause() + if (this.stopPromise) { + this.stopPromise() } - - return super.kill() - } -} - -module.exports.SoXPlayer = class extends Player { - playFile(file) { - // SoX's play command is useful for systems that don't have MPV. SoX is - // much easier to install (and probably more commonly installed, as well). - // You don't get keyboard controls such as seeking or volume adjusting - // with SoX, though. - - this.process = spawn('play', [file]) - - this.process.stdout.on('data', data => { - process.stdout.write(data.toString()) - }) - - // Most output from SoX is given to stderr, for some reason! - this.process.stderr.on('data', data => { - // The status line starts with "In:". - if (data.toString().trim().startsWith('In:')) { - if (this.disablePlaybackStatus) { - return - } - - const timeRegex = '([0-9]*):([0-9]*):([0-9]*)\.([0-9]*)' - const match = data.toString().trim().match(new RegExp( - `^In:([0-9.]+%)\\s*${timeRegex}\\s*\\[${timeRegex}\\]` - )) - - if (match) { - const percentStr = match[1] - - // SoX takes a loooooot of math in order to actually figure out the - // duration, since it outputs the current time and the remaining time - // (but not the duration). - - const [ - curHour, curMin, curSec, curSecFrac, // ##:##:##.## - remHour, remMin, remSec, remSecFrac // ##:##:##.## - ] = match.slice(2).map(n => parseInt(n)) - - const duration = Math.round( - (curHour + remHour) * 3600 + - (curMin + remMin) * 60 + - (curSec + remSec) * 1 + - (curSecFrac + remSecFrac) / 100 - ) - - const lenHour = Math.floor(duration / 3600) - const lenMin = Math.floor((duration - lenHour * 3600) / 60) - const lenSec = Math.floor(duration - lenHour * 3600 - lenMin * 60) - - this.printStatusLine(getTimeStrings({curHour, curMin, curSec, lenHour, lenMin, lenSec})) - } - } - }) - - return new Promise(resolve => { - this.process.on('close', () => resolve()) - }) + delete this.audioEl } } module.exports.getPlayer = async function() { - if (await commandExists('mpv')) { - if (await commandExists('mkfifo')) { - return new module.exports.ControllableMPVPlayer() - } else { - return new module.exports.MPVPlayer() - } - } else if (await commandExists('play')) { - return new module.exports.SoXPlayer() - } else { - return null - } + return new module.exports.WebPlayer() } |