diff options
Diffstat (limited to 'backend.js')
-rw-r--r-- | backend.js | 321 |
1 files changed, 188 insertions, 133 deletions
diff --git a/backend.js b/backend.js index 57910e9..e8601c5 100644 --- a/backend.js +++ b/backend.js @@ -28,24 +28,22 @@ const fs = require('fs') const writeFile = promisify(fs.writeFile) const readFile = promisify(fs.readFile) -class Backend extends EventEmitter { - constructor() { +class QueuePlayer extends EventEmitter { + constructor({ + getRecordFor + }) { super() this.player = null this.playingTrack = null - this.recordStore = new RecordStore() - this.throttleMetadata = throttlePromise(10) - this.metadataDictionary = {} this.queueGrouplike = {name: 'Queue', isTheQueue: true, items: []} this.pauseNextTrack = false this.playedTrackToEnd = false + this.timeData = null - this.rootDirectory = process.env.HOME + '/.mtui' - this.metadataPath = this.rootDirectory + '/track-metadata.json' + this.getRecordFor = getRecordFor } - async setup() { this.player = await getPlayer() @@ -55,79 +53,16 @@ class Backend extends EventEmitter { } } - await this.loadMetadata() - this.player.on('printStatusLine', data => { if (this.playingTrack) { - this.emit('printStatusLine', data) + this.timeData = data + this.emit('received time data', data, this) } }) return true } - - async readMetadata() { - try { - return JSON.parse(await readFile(this.metadataPath)) - } catch (error) { - // Just stop. It's okay to fail to load metadata. - return null - } - } - - async loadMetadata() { - Object.assign(this.metadataDictionary, await this.readMetadata()) - } - - async saveMetadata() { - const newData = Object.assign({}, await this.readMetadata(), this.metadataDictionary) - await writeFile(this.metadataPath, JSON.stringify(newData)) - } - - getMetadataFor(item) { - const key = this.metadataDictionary[item.downloaderArg] - return this.metadataDictionary[key] || null - } - - async processMetadata(item, reprocess = false, top = true) { - let counter = 0 - - if (isGroup(item)) { - const results = await Promise.all(item.items.map(x => this.processMetadata(x, reprocess, false))) - counter += results.reduce((acc, n) => acc + n, 0) - } else process: { - if (!reprocess && this.getMetadataFor(item)) { - break process - } - - await this.throttleMetadata(async () => { - const filePath = await this.download(item) - const metadataReader = getMetadataReaderFor(filePath) - const data = await metadataReader(filePath) - - this.metadataDictionary[item.downloaderArg] = filePath - this.metadataDictionary[filePath] = data - }) - - this.emit('processMetadata progress', this.throttleMetadata.queue.length) - - counter++ - } - - if (top) { - await this.saveMetadata() - } - - return counter - } - - - getRecordFor(item) { - return this.recordStore.getRecord(item) - } - - queue(topItem, afterItem = null, {movePlayingTrack = true} = {}) { const { items } = this.queueGrouplike const newTrackIndex = items.length @@ -389,49 +324,9 @@ class Backend extends EventEmitter { this.emit('queue updated') } - seekAhead(seconds) { - this.player.seekAhead(seconds) - } - - seekBack(seconds) { - this.player.seekBack(seconds) - } - - togglePause() { - this.player.togglePause() - } - - setPause(value) { - this.player.setPause(value) - } - - toggleLoop() { - this.player.toggleLoop() - } - - setLoop(value) { - this.player.setLoop(value) - } - - volUp(amount = 10) { - this.player.volUp(amount) - } - - volDown(amount = 10) { - this.player.volDown(amount) - } - - setVolume(value) { - this.player.setVolume(value) - } - - setPauseNextTrack(value) { - this.pauseNextTrack = !!value - } - async stopPlaying() { - // We emit this so playTrack doesn't immediately start a new track. - // We aren't *actually* about to play a new track. + // We emit this so the active play() call doesn't immediately start a new + // track. We aren't *actually* about to play a new track. this.emit('playing new track') await this.player.kill() this.clearPlayingTrack() @@ -559,10 +454,185 @@ class Backend extends EventEmitter { if (this.playingTrack !== null) { const oldTrack = this.playingTrack this.playingTrack = null + this.timeData = null this.emit('playing', null, oldTrack) } } + async download(item) { + if (isGroup(item)) { + // TODO: Download all children (recursively), show a confirmation prompt + // if there are a lot of items (remember to flatten). + return + } + + // Don't start downloading an item if we're already downloading it! + if (this.getRecordFor(item).downloading) { + return + } + + const arg = item.downloaderArg + this.getRecordFor(item).downloading = true + try { + return await getDownloaderFor(arg)(arg) + } finally { + this.getRecordFor(item).downloading = false + } + } + + seekAhead(seconds) { + this.player.seekAhead(seconds) + } + + seekBack(seconds) { + this.player.seekBack(seconds) + } + + togglePause() { + this.player.togglePause() + } + + setPause(value) { + this.player.setPause(value) + } + + toggleLoop() { + this.player.toggleLoop() + } + + setLoop(value) { + this.player.setLoop(value) + } + + volUp(amount = 10) { + this.player.volUp(amount) + } + + volDown(amount = 10) { + this.player.volDown(amount) + } + + setVolume(value) { + this.player.setVolume(value) + } + + setPauseNextTrack(value) { + this.pauseNextTrack = !!value + } + + get playSymbol() { + if (this.player && this.playingTrack) { + if (this.player.isPaused) { + return '⏸' + } else { + return '▶' + } + } else { + return '.' + } + } +} + +class Backend extends EventEmitter { + constructor() { + super() + + this.queuePlayers = [] + + this.recordStore = new RecordStore() + this.throttleMetadata = throttlePromise(10) + this.metadataDictionary = {} + + this.rootDirectory = process.env.HOME + '/.mtui' + this.metadataPath = this.rootDirectory + '/track-metadata.json' + } + + async setup() { + const error = await this.addQueuePlayer() + if (error.error) { + return error + } + + await this.loadMetadata() + + return true + } + + async addQueuePlayer() { + const queuePlayer = new QueuePlayer({ + getRecordFor: item => this.getRecordFor(item) + }) + + const error = await queuePlayer.setup() + if (error.error) { + return error + } + + this.queuePlayers.push(queuePlayer) + this.emit('added queue player', queuePlayer) + + return queuePlayer + } + + async readMetadata() { + try { + return JSON.parse(await readFile(this.metadataPath)) + } catch (error) { + // Just stop. It's okay to fail to load metadata. + return null + } + } + + async loadMetadata() { + Object.assign(this.metadataDictionary, await this.readMetadata()) + } + + async saveMetadata() { + const newData = Object.assign({}, await this.readMetadata(), this.metadataDictionary) + await writeFile(this.metadataPath, JSON.stringify(newData)) + } + + getMetadataFor(item) { + const key = this.metadataDictionary[item.downloaderArg] + return this.metadataDictionary[key] || null + } + + async processMetadata(item, reprocess = false, top = true) { + let counter = 0 + + if (isGroup(item)) { + const results = await Promise.all(item.items.map(x => this.processMetadata(x, reprocess, false))) + counter += results.reduce((acc, n) => acc + n, 0) + } else process: { + if (!reprocess && this.getMetadataFor(item)) { + break process + } + + await this.throttleMetadata(async () => { + const filePath = await this.download(item) + const metadataReader = getMetadataReaderFor(filePath) + const data = await metadataReader(filePath) + + this.metadataDictionary[item.downloaderArg] = filePath + this.metadataDictionary[filePath] = data + }) + + this.emit('processMetadata progress', this.throttleMetadata.queue.length) + + counter++ + } + + if (top) { + await this.saveMetadata() + } + + return counter + } + + getRecordFor(item) { + return this.recordStore.getRecord(item) + } + getDuration(item) { let noticedMissingMetadata = false @@ -588,24 +658,9 @@ class Backend extends EventEmitter { return {seconds, string, noticedMissingMetadata, approxSymbol} } - async download(item) { - if (isGroup(item)) { - // TODO: Download all children (recursively), show a confirmation prompt - // if there are a lot of items (remember to flatten). - return - } - - // Don't start downloading an item if we're already downloading it! - if (this.getRecordFor(item).downloading) { - return - } - - const arg = item.downloaderArg - this.getRecordFor(item).downloading = true - try { - return await getDownloaderFor(arg)(arg) - } finally { - this.getRecordFor(item).downloading = false + async stopPlayingAll() { + for (const queuePlayer of this.queuePlayers) { + await queuePlayer.stopPlaying() } } } |