diff options
Diffstat (limited to 'backend.js')
-rw-r--r-- | backend.js | 220 |
1 files changed, 126 insertions, 94 deletions
diff --git a/backend.js b/backend.js index 0f15a43..6576868 100644 --- a/backend.js +++ b/backend.js @@ -3,32 +3,29 @@ 'use strict' -const { getDownloaderFor } = require('./downloaders') -const { getMetadataReaderFor } = require('./metadata-readers') -const { getPlayer } = require('./players') -const RecordStore = require('./record-store') -const os = require('os') -const shortid = require('shortid') - -const { +import {readFile, writeFile} from 'node:fs/promises' +import EventEmitter from 'node:events' +import os from 'node:os' + +import shortid from 'shortid' + +import {getDownloaderFor} from './downloaders.js' +import {getMetadataReaderFor} from './metadata-readers.js' +import {getPlayer} from './players.js' +import RecordStore from './record-store.js' + +import { getTimeStringsFromSec, shuffleArray, - throttlePromise -} = require('./general-util') + throttlePromise, +} from './general-util.js' -const { +import { isGroup, isTrack, flattenGrouplike, - getItemPathString, - parentSymbol -} = require('./playlist-utils') - -const { promisify } = require('util') -const EventEmitter = require('events') -const fs = require('fs') -const writeFile = promisify(fs.writeFile) -const readFile = promisify(fs.readFile) + parentSymbol, +} from './playlist-utils.js' async function download(item, record) { if (isGroup(item)) { @@ -69,7 +66,7 @@ class QueuePlayer extends EventEmitter { this.playingTrack = null this.queueGrouplike = {name: 'Queue', isTheQueue: true, items: []} this.pauseNextTrack = false - this.loopQueueAtEnd = false + this.queueEndMode = 'end' // end, loop, shuffle this.playedTrackToEnd = false this.timeData = null this.time = null @@ -92,9 +89,10 @@ class QueuePlayer extends EventEmitter { this.player.on('printStatusLine', data => { if (this.playingTrack) { + const oldTimeData = this.timeData this.timeData = data this.time = data.curSecTotal - this.emit('received time data', data, this) + this.emit('received time data', data, oldTimeData) } }) @@ -217,20 +215,6 @@ class QueuePlayer extends EventEmitter { const distributeSize = distributeEnd - distributeStart - const queueItem = (item, insertIndex) => { - if (items.includes(item)) { - /* - if (!movePlayingTrack && item === this.playingTrack) { - return - } - */ - items.splice(items.indexOf(item), 1) - } else { - offset++ - } - items.splice(insertIndex, 0, item) - } - if (how === 'evenly') { let offset = 0 for (const item of newTracks) { @@ -249,7 +233,7 @@ class QueuePlayer extends EventEmitter { } } - this.emit('distribute-queue', topItem, {how, rangeEnd}) + this.emit('distribute queue', topItem, {how, rangeEnd}) this.emitQueueUpdated() } @@ -317,7 +301,7 @@ class QueuePlayer extends EventEmitter { items.splice(index) } - this.emit('clear-queue-past', track) + this.emit('clear queue past', track) this.emitQueueUpdated() } @@ -334,7 +318,7 @@ class QueuePlayer extends EventEmitter { items.splice(startIndex, endIndex - startIndex) } - this.emit('clear-queue-up-to', track) + this.emit('clear queue up to', track) this.emitQueueUpdated() } @@ -358,14 +342,16 @@ class QueuePlayer extends EventEmitter { } } - shuffleQueue() { + shuffleQueue(pastPlayingTrackOnly = true) { const queue = this.queueGrouplike - const index = queue.items.indexOf(this.playingTrack) + 1 // This is 0 if no track is playing + const index = (pastPlayingTrackOnly + ? queue.items.indexOf(this.playingTrack) + 1 // This is 0 if no track is playing + : 0) const initialItems = queue.items.slice(0, index) const remainingItems = queue.items.slice(index) const newItems = initialItems.concat(shuffleArray(remainingItems)) queue.items = newItems - this.emit('shuffle-queue') + this.emit('shuffle queue') this.emitQueueUpdated() } @@ -374,7 +360,7 @@ class QueuePlayer extends EventEmitter { // the track that's currently playing). this.queueGrouplike.items = this.queueGrouplike.items .filter(item => item === this.playingTrack) - this.emit('clear-queue') + this.emit('clear queue') this.emitQueueUpdated() } @@ -390,8 +376,7 @@ class QueuePlayer extends EventEmitter { this.clearPlayingTrack() } - - async play(item, forceStartPaused) { + async play(item, startTime = 0, forceStartPaused = false) { if (this.player === null) { throw new Error('Attempted to play before a player was loaded') } @@ -439,8 +424,8 @@ class QueuePlayer extends EventEmitter { this.timeData = null this.time = null this.playingTrack = item - this.emit('playing details', this.playingTrack, oldTrack, this) - this.emit('playing', this.playingTrack, oldTrack, this) + this.emit('playing details', this.playingTrack, oldTrack, startTime) + this.emit('playing', this.playingTrack) await this.player.kill() if (this.alwaysStartPaused || forceStartPaused) { @@ -452,7 +437,7 @@ class QueuePlayer extends EventEmitter { } else { this.player.setPause(false) } - await this.player.playFile(downloadFile) + await this.player.playFile(downloadFile, startTime) } // playingThisTrack now means whether the track played through to the end @@ -462,13 +447,7 @@ class QueuePlayer extends EventEmitter { this.playedTrackToEnd = true this.emit('done playing', this.playingTrack) if (!this.waitWhenDonePlaying) { - if (!this.playNext(item)) { - if (this.loopQueueAtEnd) { - this.playFirst() - } else { - this.clearPlayingTrack() - } - } + this.playNext(item) } } } @@ -476,6 +455,12 @@ class QueuePlayer extends EventEmitter { playNext(track, automaticallyQueueNextTrack = false) { if (!track) return false + // Auto-queue is nice but it should only happen when the queue hasn't been + // explicitly set to loop. + automaticallyQueueNextTrack = ( + automaticallyQueueNextTrack && + this.queueEndMode === 'end') + const queue = this.queueGrouplike let queueIndex = queue.items.indexOf(track) if (queueIndex === -1) return false @@ -494,7 +479,7 @@ class QueuePlayer extends EventEmitter { this.queue(nextItem) queueIndex = queue.items.length - 1 } else { - return false + return this.playNextAtQueueEnd() } } @@ -540,14 +525,51 @@ class QueuePlayer extends EventEmitter { return false } + playNextAtQueueEnd() { + switch (this.queueEndMode) { + case 'loop': + this.playFirst() + return true + case 'shuffle': + this.shuffleQueue(false) + this.playFirst() + return true + case 'end': + default: + this.clearPlayingTrack() + return false + } + } + + async playOrSeek(item, time) { + if (!isTrack(item)) { + // This only makes sense to call with individual tracks! + return + } + + if (item === this.playingTrack) { + this.seekTo(time) + } else { + // Queue the track, but only if it's not already in the queue, so that we + // respect an existing queue order. + const queue = this.queueGrouplike + const queueIndex = queue.items.indexOf(item) + if (queueIndex === -1) { + this.queue(item, this.playingTrack) + } + + this.play(item, time) + } + } + clearPlayingTrack() { if (this.playingTrack !== null) { const oldTrack = this.playingTrack this.playingTrack = null this.timeData = null this.time = null - this.emit('playing details', null, oldTrack, this) - this.emit('playing', null, oldTrack, this) + this.emit('playing details', null, oldTrack, 0) + this.emit('playing', null) } } @@ -558,7 +580,7 @@ class QueuePlayer extends EventEmitter { seekAhead(seconds) { this.time += seconds this.player.seekAhead(seconds) - this.emit('seek-ahead', +seconds) + this.emit('seek ahead', +seconds) } seekBack(seconds) { @@ -568,48 +590,56 @@ class QueuePlayer extends EventEmitter { this.time -= seconds } this.player.seekBack(seconds) - this.emit('seek-back', +seconds) + this.emit('seek back', +seconds) } seekTo(timeInSecs) { this.time = timeInSecs this.player.seekTo(timeInSecs) - this.emit('seek-to', +timeInSecs) + this.emit('seek to', +timeInSecs) + } + + seekTo(seconds) { + this.player.seekTo(seconds) + } + + seekToStart() { + this.player.seekToStart() } togglePause() { this.player.togglePause() - this.emit('toggle-pause') + this.emit('toggle pause') } setPause(value) { this.player.setPause(value) - this.emit('set-pause', !!value) + this.emit('set pause', !!value) } toggleLoop() { this.player.toggleLoop() - this.emit('toggle-loop') + this.emit('toggle loop') } setLoop(value) { this.player.setLoop(value) - this.emit('set-loop', !!value) + this.emit('set loop', !!value) } - volUp(amount = 10) { + volumeUp(amount = 10) { this.player.volUp(amount) - this.emit('vol-up', +amount) + this.emit('volume up', +amount) } - volDown(amount = 10) { + volumeDown(amount = 10) { this.player.volDown(amount) - this.emit('vol-down', +amount) + this.emit('volume down', +amount) } setVolume(value) { this.player.setVolume(value) - this.emit('set-volume', +value) + this.emit('set volume', +value) } setVolumeMultiplier(value) { @@ -622,12 +652,12 @@ class QueuePlayer extends EventEmitter { setPauseNextTrack(value) { this.pauseNextTrack = !!value - this.emit('set-pause-next-track', !!value) + this.emit('set pause next track', !!value) } setLoopQueueAtEnd(value) { this.loopQueueAtEnd = !!value - this.emit('set-loop-queue-at-end', !!value) + this.emit('set loop queue at end', !!value) } get remainingTracks() { @@ -653,7 +683,7 @@ class QueuePlayer extends EventEmitter { } } -class Backend extends EventEmitter { +export default class Backend extends EventEmitter { constructor({ playerName = null, playerOptions = [] @@ -675,6 +705,7 @@ class Backend extends EventEmitter { this.sharedSourcesMap = Object.create(null) this.sharedSourcesGrouplike = { name: 'Shared Sources', + isPartySources: true, items: [] } @@ -715,30 +746,33 @@ class Backend extends EventEmitter { this.emit('added queue player', queuePlayer) for (const event of [ - 'playing', + 'clear queue', + 'clear queue past', + 'clear queue up to', + 'distribute queue', 'done playing', + 'playing', + 'playing details', 'queue', - 'distribute-queue', - 'unqueue', - 'clear-queue-past', - 'clear-queue-up-to', - 'shuffle-queue', - 'clear-queue', 'queue updated', - 'seek-ahead', - 'seek-back', - 'toggle-pause', - 'set-pause', - 'toggle-loop', - 'set-loop', - 'vol-up', - 'vol-down', - 'set-volume', - 'set-pause-next-track', - 'set-loop-queue-at-end' + 'received time data', + 'seek ahead', + 'seek back', + 'seek to', + 'set loop', + 'set loop queue at end', + 'set pause', + 'set pause next track', + 'set volume', + 'shuffle queue', + 'toggle loop', + 'toggle pause', + 'unqueue', + 'volume down', + 'volume up', ]) { queuePlayer.on(event, (...data) => { - this.emit(event, queuePlayer, ...data) + this.emit('QP: ' + event, queuePlayer, ...data) }) } @@ -899,5 +933,3 @@ class Backend extends EventEmitter { this.emit('share with party', item) } } - -module.exports = Backend |