'use strict'; var MediaModel = require('./models/mediaModel.js'), PlayModel = require('./models/playModel.js'); var DubAPIError = require('./errors/error.js'), DubAPIRequestError = require('./errors/requestError.js'); var endpoints = require('./data/endpoints.js'), events = require('./data/events.js'), utils = require('./utils.js'); var mediaCache = {}; function ActionHandler(dubAPI, auth) { this.doLogin = doLogin.bind(dubAPI, auth); this.clearPlay = clearPlay.bind(dubAPI); this.updatePlay = updatePlay.bind(dubAPI); this.updateQueue = updateQueue.bind(dubAPI); this.updateQueueDebounce = utils.debounce(this.updateQueue, 5000); } function doLogin(auth, callback) { var that = this; this._.reqHandler.send({method: 'POST', url: endpoints.authDubtrack, form: auth}, function(code, body) { if ([200, 400].indexOf(code) === -1) { return callback(new DubAPIRequestError(code, that._.reqHandler.endpoint(endpoints.authDubtrack))); } else if (code === 400) { return callback(new DubAPIError('Authentication Failed: ' + body.data.details.message)); } callback(undefined); }); } function clearPlay() { clearTimeout(this._.room.playTimeout); var message = {type: events.roomPlaylistUpdate}; if (this._.room.play) { message.lastPlay = { id: this._.room.play.id, media: utils.clone(this._.room.play.media), user: utils.clone(this._.room.users.findWhere({id: this._.room.play.user})), score: this._.room.play.getScore() }; } this._.room.play = undefined; this._.room.users.set({dub: undefined}); this.emit('*', message); this.emit(events.roomPlaylistUpdate, message); } function updatePlay() { var that = this; clearTimeout(that._.room.playTimeout); that._.reqHandler.queue({method: 'GET', url: endpoints.roomPlaylistActive}, function(code, body) { if ([200, 404].indexOf(code) === -1) { that.emit('error', new DubAPIRequestError(code, that._.reqHandler.endpoint(endpoints.roomPlaylistActive))); return; } else if (code === 404) { that._.actHandler.clearPlay(); return; } if (body.data.song === undefined) { //Dubtrack API sometimes doesn't define a song. //Schedule an update in the future, maybe it will then. that._.room.playTimeout = setTimeout(that._.actHandler.updatePlay, 30000); return; } var message = {type: events.roomPlaylistUpdate}, newPlay = new PlayModel(body.data.song), curPlay = that._.room.play; if (curPlay && newPlay.id === curPlay.id) { if (Date.now() - newPlay.played > newPlay.songLength) that._.actHandler.clearPlay(); return; } // 20210510 QueUp API sometimes doesn't define songInfo if (body.data.songInfo != null) { newPlay.media = new MediaModel(body.data.songInfo); if (newPlay.media.type === undefined || newPlay.media.fkid === undefined) { newPlay.media = undefined; } } message.raw = body.data; if (that._.room.play) { message.lastPlay = { id: curPlay.id, media: utils.clone(curPlay.media), user: utils.clone(that._.room.users.findWhere({id: curPlay.user})), score: curPlay.getScore() }; } message.id = newPlay.id; message.media = utils.clone(newPlay.media); message.user = utils.clone(that._.room.users.findWhere({id: newPlay.user})); that._.reqHandler.queue({method: 'GET', url: endpoints.roomPlaylistActiveDubs}, function(code, body) { that._.room.play = newPlay; that._.room.playTimeout = setTimeout(that._.actHandler.updatePlay, newPlay.getTimeRemaining() + 15000); that._.room.users.set({dub: undefined}); if (code === 200) { body.data.currentSong = new PlayModel(body.data.currentSong); if (newPlay.id === body.data.currentSong.id) { newPlay.updubs = body.data.currentSong.updubs; newPlay.downdubs = body.data.currentSong.downdubs; newPlay.grabs = body.data.currentSong.grabs; body.data.upDubs.forEach(function(dub) { newPlay.dubs[dub.userid] = 'updub'; var user = that._.room.users.findWhere({id: dub.userid}); if (user) user.set({dub: 'updub'}); }); body.data.downDubs.forEach(function(dub) { newPlay.dubs[dub.userid] = 'downdub'; var user = that._.room.users.findWhere({id: dub.userid}); if (user) user.set({dub: 'downdub'}); }); } } else { that.emit('error', new DubAPIRequestError(code, that._.reqHandler.endpoint(endpoints.roomPlaylistActiveDubs))); } that.emit('*', message); that.emit(events.roomPlaylistUpdate, message); }); }); } function updateQueue() { var that = this; that._.reqHandler.queue({method: 'GET', url: endpoints.roomPlaylistDetails}, function(code, body) { if (code !== 200) { that.emit('error', new DubAPIRequestError(code, that._.reqHandler.endpoint(endpoints.roomPlaylistDetails))); return; } var promises = body.data.reduce(function(promises, queueItem) { if (queueItem === null) return promises; if (mediaCache[queueItem.songid]) { mediaCache[queueItem.songid].ts = Date.now(); return promises; } promises.push(new Promise(function(resolve, reject) { that._.reqHandler.send({method: 'GET', url: endpoints.song.replace('%SONGID%', queueItem.songid)}, function(code, body) { if (code !== 200) { that.emit('error', new DubAPIRequestError(code, that._.reqHandler.endpoint(endpoints.song.replace('%SONGID%', queueItem.songid)))); // Yes this should probably reject, but that changes the function of Promise.all // This result is not used, we're simply awaiting completion of all the requests return resolve(); } mediaCache[queueItem.songid] = {media: new MediaModel(body.data), ts: Date.now()}; return resolve(); }); })); return promises; }, []); Promise.all(promises).then(function() { var message = {type: events.roomPlaylistQueueUpdate}; that._.room.queue.clear(); body.data.forEach(function(queueItem) { //Dubtrack API sometimes returns an array containing null. if (queueItem === null) return; queueItem = { id: queueItem._id, uid: queueItem._user._id, media: mediaCache[queueItem.songid] ? mediaCache[queueItem.songid].media : undefined, get user() {return that._.room.users.findWhere({id: this.uid});} }; that._.room.queue.add(queueItem); }); message.raw = body.data; message.queue = utils.clone(that._.room.queue, {deep: true}); that.emit('*', message); that.emit(events.roomPlaylistQueueUpdate, message); }); setImmediate(function() { for (var key in mediaCache) { if (mediaCache.hasOwnProperty(key) && Date.now() - mediaCache[key].ts > 900000) delete mediaCache[key]; } }); }); } module.exports = ActionHandler;