Create PlayLog, move tag identification into MediaLibrary

This commit is contained in:
Annika Backstrom 2017-04-09 22:05:52 -04:00
parent 78f2802dea
commit 43ad5281e3
7 changed files with 150 additions and 73 deletions

View File

@ -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();
});
}
}

View File

@ -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;

View File

@ -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:

45
jukebox/library/tags.js Normal file
View File

@ -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
};

View File

@ -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);
}
}
}

View File

@ -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({