diff --git a/jukebox/library/file-backend.js b/jukebox/library/file-backend.js index 5b2e880..fb32d97 100644 --- a/jukebox/library/file-backend.js +++ b/jukebox/library/file-backend.js @@ -1,6 +1,7 @@ "use strict"; const glob = require('glob'); +const basename = require('path').basename; class FileBackend { constructor(config) { @@ -10,8 +11,9 @@ class FileBackend { find(tag, callback) { glob('media/' + tag + ' - *.mp3', (err, files) => { if (files.length > 0) { - callback(files[0]); + callback(basename(files[0])); } + callback(); }); } } diff --git a/jukebox/library/index.js b/jukebox/library/index.js index 437de26..1bd2484 100644 --- a/jukebox/library/index.js +++ b/jukebox/library/index.js @@ -1,70 +1,34 @@ "use strict"; const EventEmitter = require('events').EventEmitter; +const tags = require('./tags'); class Library extends EventEmitter { constructor(config, backend) { super(); - this.log_size = 10; this.config = config; this.backend = backend; - this.log = []; } find(tag) { + if (tag === this.config.stop_id) { + this.emit('action', new tags.StopCommand()); + } + + if (tag === this.config.pause_id) { + this.emit('action', new tags.PauseCommand()); + } + this.backend.find(tag, path => { if (!path) { - this.updateLog(new NotFoundLogEntry(tag)); + this.emit('action', new tags.NotFoundTag(tag)); return; } - this.updateLog(new FileLogEntry(tag, path)); + this.emit('action', new tags.FileTag(tag, path)); }); } - - updateLog(entry) { - this.log.unshift(entry); - if (this.log.length > this.log_size) { - this.log.pop(); - } - this.emit('play', entry); - } - - getLastTag() { - if (this.log.length == 0) { - return; - } - - return this.log[0].tag; - } - - getLog() { - return this.log; - } -} - -class LogEntry { - constructor(tag) { - this.tag = tag; - } - - toString() { - return this.tag; - } -} - -class NotFoundLogEntry extends LogEntry { - toString() { - return `Unknown media: ${this.tag}` - } -} - -class FileLogEntry extends LogEntry { - constructor(tag, path) { - super(tag); - this.path = path; - } } module.exports.Library = Library; diff --git a/jukebox/library/play-log.js b/jukebox/library/play-log.js new file mode 100644 index 0000000..5d406cf --- /dev/null +++ b/jukebox/library/play-log.js @@ -0,0 +1,44 @@ +"use strict"; + +const EventEmitter = require('events').EventEmitter; + +const tags = require('./tags'); + +module.exports.PlayLog = class PlayLog extends EventEmitter { + constructor() { + super(); + this.last_tag = undefined; + this.log = []; + this.log_size = 10; + } + + updateLog(tag) { + this.last_tag = tag; + + if (tag instanceof tags.NotFoundTag) { + this.emit('update', tag); + return; + } + + this.log.unshift(tag); + if (this.log.length > this.log_size) { + this.log.pop(); + } + + this.emit('update', tag); + } + + getLastTag() { + if (this.log.length == 0) { + return; + } + + return this.log[0].tag; + } + + getLog() { + return this.log; + } +} + +// vim:ts=2 sw=2 et: diff --git a/jukebox/library/tags.js b/jukebox/library/tags.js new file mode 100644 index 0000000..0e62455 --- /dev/null +++ b/jukebox/library/tags.js @@ -0,0 +1,45 @@ +class Tag { + constructor(tag) { + this.tag = tag; + } + + toString() { + return this.tag; + } +} + +class StopCommand extends Tag { + toString() { + return "Command: STOP"; + } +} + +class PauseCommand extends Tag { + toString() { + return "Command: PAUSE"; + } +} + +class NotFoundTag extends Tag { + toString() { + return `Unknown tag: ${this.tag}` + } +} + +class FileTag extends Tag { + constructor(tag, path) { + super(tag); + this.path = path; + } + + toString() { + return this.path; + } +} + +module.exports = { + StopCommand: StopCommand + , PauseCommand: PauseCommand + , NotFoundTag: NotFoundTag + , FileTag: FileTag +}; diff --git a/jukebox/media-player.js b/jukebox/media-player.js index 72d6563..53db6a6 100644 --- a/jukebox/media-player.js +++ b/jukebox/media-player.js @@ -1,11 +1,14 @@ "use strict"; +const tags = require('./library/tags'); + const throttle = require('throttle-debounce/throttle'); const ChildProcessEmitter = require('./child-process').ChildProcessEmitter; const DEFAULT_PAUSE_THROTTLE = 2000; const DEFAULT_PLAY_THROTTLE = 5000; +const DEFAULT_UNKNOWN_THROTTLE = 2000; class MediaPlayer extends ChildProcessEmitter { constructor(config, logger) { @@ -23,6 +26,8 @@ class MediaPlayer extends ChildProcessEmitter { this._playFile(path); }); + this.unknown = throttle(config.unknown_throttle || DEFAULT_UNKNOWN_THROTTLE, this._unknown); + // default to unthrottled this.pause = this._pauseFile; this.playFile = this._playFile; @@ -33,16 +38,35 @@ class MediaPlayer extends ChildProcessEmitter { this.playFile = this.playFileThrottled; } - stop() { + stop(tag) { + this.emit('command', tag); this.send('STOP'); } - _pause() { + _pause(tag) { + this.emit('command', tag); this.send('PAUSE'); } - _playFile(path) { - this.send("LOAD " + path); + _playFile(tag) { + this.emit('command', tag); + this.send("LOAD media/" + tag.path); + } + + _unknown(tag) { + this.emit('command', tag); + } + + handleTag(tag) { + if (tag instanceof tags.StopCommand) { + this.stop(tag); + } else if (tag instanceof tags.PauseCommand) { + this.pause(tag); + } else if (tag instanceof tags.FileTag) { + this.playFile(tag); + } else { + this.unknown(tag); + } } } diff --git a/jukebox/tag-reader.js b/jukebox/tag-reader.js index fc168cb..e87b91a 100644 --- a/jukebox/tag-reader.js +++ b/jukebox/tag-reader.js @@ -3,9 +3,9 @@ const ChildProcessEmitter = require('./child-process').ChildProcessEmitter; class TagReader extends ChildProcessEmitter { - constructor(config, logger) { - super(config.tag_reader, logger); - } + constructor(config, logger) { + super(config.tag_reader, logger); + } } module.exports = function(config, logger) { diff --git a/player.js b/player.js index ef58a3f..0592fa9 100644 --- a/player.js +++ b/player.js @@ -22,6 +22,9 @@ logger.level = config.debug ? 'debug' : 'info'; const MediaPlayer = require('./jukebox/media-player')(config, logger); const TagReader = require('./jukebox/tag-reader')(config, logger); +const play_log = require('./jukebox/library/play-log'); + +const PlayLog = new play_log.PlayLog(); MediaPlayer.throttle(); @@ -36,6 +39,9 @@ function exitHandler(options, err) { logger.debug('closing child tasks'); if (err) logger.debug(err.stack); + // FIXME When do we actually need to call this? Adding these seems + // to have fixed a problem with orphaned children, but not we get + // two sets of "closing child tasks" messages when we shut down. TagReader.kill(); MediaPlayer.kill(); @@ -57,28 +63,20 @@ if (config.debug) { }); } -MediaLibrary.on('play', event => { +MediaPlayer.on('command', tag => { + PlayLog.updateLog(tag); +}); + +PlayLog.on('update', tag => { var data = { - html: views.log.render({ play_log: MediaLibrary.getLog() }), - tag: MediaLibrary.getLastTag() + html: views.log.render({ play_log: PlayLog.getLog() }), + tag: tag.tag }; wss.broadcast(JSON.stringify(data)); }); -MediaLibrary.on('play', event => { - if (event.tag === config.stop_id) { - MediaPlayer.stop(); - } - - if (event.tag === config.pause_id) { - MediaPlayer.pause(); - } - - if (event.path) { - MediaPlayer.playFile(event.path); - } -}); +MediaLibrary.on('action', MediaPlayer.handleTag.bind(MediaPlayer)); app.use(morgan('dev')) app.use(express.static(__dirname + '/static')) @@ -86,9 +84,9 @@ app.use(express.static(__dirname + '/static')) app.get('/', function (req, res, next) { try { var index = views.index.render({ - last_tag: MediaLibrary.getLastTag() + last_tag: PlayLog.getLastTag() , config: config - , play_log: MediaLibrary.getLog() + , play_log: PlayLog.getLog() }); var html = views.base.render({