Added persistent rescheduling of nowPlaying after server goes down.
This commit is contained in:
parent
179a10fb72
commit
a41541d07b
|
|
@ -19,6 +19,7 @@ const connectedUser = require('./connectedUser');
|
||||||
const queue = require('./media/queue');
|
const queue = require('./media/queue');
|
||||||
const flairModel = require('../../schemas/flairSchema');
|
const flairModel = require('../../schemas/flairSchema');
|
||||||
const permissionModel = require('../../schemas/permissionSchema');
|
const permissionModel = require('../../schemas/permissionSchema');
|
||||||
|
const channelModel = require('../../schemas/channel/channelSchema');
|
||||||
|
|
||||||
module.exports = class{
|
module.exports = class{
|
||||||
constructor(server, chanDB){
|
constructor(server, chanDB){
|
||||||
|
|
@ -27,7 +28,7 @@ module.exports = class{
|
||||||
this.tokeCommands = chanDB.tokeCommands;
|
this.tokeCommands = chanDB.tokeCommands;
|
||||||
//Keeping these in a map was originally a vestige but it's more preformant than an array or object so :P
|
//Keeping these in a map was originally a vestige but it's more preformant than an array or object so :P
|
||||||
this.userList = new Map();
|
this.userList = new Map();
|
||||||
this.queue = new queue(server, this);
|
this.queue = new queue(server, chanDB, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleConnection(userDB, chanDB, socket){
|
async handleConnection(userDB, chanDB, socket){
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ const loggerUtils = require('../../../utils/loggerUtils');
|
||||||
const channelModel = require('../../../schemas/channel/channelSchema');
|
const channelModel = require('../../../schemas/channel/channelSchema');
|
||||||
|
|
||||||
module.exports = class{
|
module.exports = class{
|
||||||
constructor(server, channel){
|
constructor(server, chanDB, channel){
|
||||||
//Set server
|
//Set server
|
||||||
this.server = server
|
this.server = server
|
||||||
//Set channel
|
//Set channel
|
||||||
|
|
@ -47,6 +47,9 @@ module.exports = class{
|
||||||
|
|
||||||
//create boolean to hold schedule lock
|
//create boolean to hold schedule lock
|
||||||
this.locked = false;
|
this.locked = false;
|
||||||
|
|
||||||
|
//Rehydrate channel queue from database
|
||||||
|
this.rehydrateQueue(chanDB);
|
||||||
}
|
}
|
||||||
|
|
||||||
defineListeners(socket){
|
defineListeners(socket){
|
||||||
|
|
@ -441,8 +444,8 @@ module.exports = class{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//If the item has already started and it's not being forced
|
//If the item has already started
|
||||||
if((mediaObj.startTime < new Date().getTime())){
|
if((mediaObj.startTime < new Date().getTime()) && !force){
|
||||||
//Set time stamp to existing timestamp plus the difference between the orginal start-date and now
|
//Set time stamp to existing timestamp plus the difference between the orginal start-date and now
|
||||||
mediaObj.startTimeStamp = mediaObj.startTimeStamp + ((new Date().getTime() - mediaObj.startTime) / 1000)
|
mediaObj.startTimeStamp = mediaObj.startTimeStamp + ((new Date().getTime() - mediaObj.startTime) / 1000)
|
||||||
//Start the item now
|
//Start the item now
|
||||||
|
|
@ -493,7 +496,7 @@ module.exports = class{
|
||||||
return mediaObj;
|
return mediaObj;
|
||||||
}
|
}
|
||||||
|
|
||||||
start(mediaObj, timestamp = mediaObj.startTimeStamp){
|
async start(mediaObj, timestamp = mediaObj.startTimeStamp){
|
||||||
//Silently end the media
|
//Silently end the media
|
||||||
this.end(true);
|
this.end(true);
|
||||||
|
|
||||||
|
|
@ -503,6 +506,19 @@ module.exports = class{
|
||||||
//Set current playing media
|
//Set current playing media
|
||||||
this.nowPlaying = mediaObj;
|
this.nowPlaying = mediaObj;
|
||||||
|
|
||||||
|
try{
|
||||||
|
//Get our channel
|
||||||
|
const chanDB = await channelModel.findOne({name: this.channel.name});
|
||||||
|
|
||||||
|
//Set the now playing queued media document
|
||||||
|
chanDB.media.nowPlaying = mediaObj;
|
||||||
|
|
||||||
|
//Save the channel
|
||||||
|
await chanDB.save();
|
||||||
|
}catch(err){
|
||||||
|
loggerUtils.localExceptionHandler(err);
|
||||||
|
}
|
||||||
|
|
||||||
//Send play signal out to the channel
|
//Send play signal out to the channel
|
||||||
this.sendMedia();
|
this.sendMedia();
|
||||||
|
|
||||||
|
|
@ -655,4 +671,31 @@ module.exports = class{
|
||||||
broadcastQueue(){
|
broadcastQueue(){
|
||||||
this.server.io.in(this.channel.name).emit('queue',{queue: Array.from(this.schedule)})
|
this.server.io.in(this.channel.name).emit('queue',{queue: Array.from(this.schedule)})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async rehydrateQueue(chanDB){
|
||||||
|
try{
|
||||||
|
//If we didn't get handed a freebie
|
||||||
|
if(chanDB == null){
|
||||||
|
//Go out and get it done ourselves
|
||||||
|
chanDB = await channelModel.findOne({name:this.channel.name});
|
||||||
|
}
|
||||||
|
|
||||||
|
//If we couldn't find the channel
|
||||||
|
if(chanDB == null){
|
||||||
|
//FUCK
|
||||||
|
throw new Error(`Unable to find channel document ${this.channel.name} while rehydrating queue!`);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Rehydrate the currently playing item
|
||||||
|
const wasPlaying = chanDB.media.nowPlaying.rehydrate();
|
||||||
|
|
||||||
|
//Schedule it
|
||||||
|
this.scheduleMedia(wasPlaying, null, true);
|
||||||
|
|
||||||
|
//if something fucked up
|
||||||
|
}catch(err){
|
||||||
|
//bitch about it in the server console
|
||||||
|
loggerUtils.localExceptionHandler(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -18,7 +18,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.*/
|
||||||
const media = require('./media');
|
const media = require('./media');
|
||||||
|
|
||||||
module.exports = class extends media{
|
module.exports = class extends media{
|
||||||
constructor(title, fileName, url, id, type, duration, startTime, startTimeStamp){
|
constructor(title, fileName, url, id, type, duration, startTime, startTimeStamp, earlyEnd, uuid){
|
||||||
//Call derived constructor
|
//Call derived constructor
|
||||||
super(title, fileName, url, id, type, duration);
|
super(title, fileName, url, id, type, duration);
|
||||||
//Set media start time
|
//Set media start time
|
||||||
|
|
@ -27,10 +27,17 @@ module.exports = class extends media{
|
||||||
this.startTimeStamp = startTimeStamp;
|
this.startTimeStamp = startTimeStamp;
|
||||||
//Create empty variable to hold early end if media is stopped early
|
//Create empty variable to hold early end if media is stopped early
|
||||||
this.earlyEnd = null;
|
this.earlyEnd = null;
|
||||||
|
//Set status for discriminator key
|
||||||
|
this.status = 'queued';
|
||||||
|
|
||||||
//Generate id unique to this specific entry of this specific file within this specific channel's queue
|
//If we have a null uuid (can't use default argument because of 'this')
|
||||||
//That way even if we have six copies of the same video queued, we can still uniquely idenitify each instance
|
if(uuid == null){
|
||||||
this.genUUID();
|
//Generate id unique to this specific entry of this specific file within this specific channel's queue
|
||||||
|
//That way even if we have six copies of the same video queued, we can still uniquely idenitify each instance
|
||||||
|
this.genUUID();
|
||||||
|
}else{
|
||||||
|
this.uuid = uuid;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//statics
|
//statics
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ const emoteModel = require('../emoteSchema');
|
||||||
//DB Schemas
|
//DB Schemas
|
||||||
const channelPermissionSchema = require('./channelPermissionSchema');
|
const channelPermissionSchema = require('./channelPermissionSchema');
|
||||||
const channelBanSchema = require('./channelBanSchema');
|
const channelBanSchema = require('./channelBanSchema');
|
||||||
|
const queuedMediaSchema = require('./media/queuedMediaSchema');
|
||||||
//Utils
|
//Utils
|
||||||
const { exceptionHandler, errorHandler } = require('../../utils/loggerUtils');
|
const { exceptionHandler, errorHandler } = require('../../utils/loggerUtils');
|
||||||
|
|
||||||
|
|
@ -98,6 +99,11 @@ const channelSchema = new mongoose.Schema({
|
||||||
default: emoteModel.typeEnum[0]
|
default: emoteModel.typeEnum[0]
|
||||||
}
|
}
|
||||||
}],
|
}],
|
||||||
|
media: {
|
||||||
|
nowPlaying: queuedMediaSchema,
|
||||||
|
scheduled: [queuedMediaSchema],
|
||||||
|
archived: [queuedMediaSchema],
|
||||||
|
},
|
||||||
//Thankfully we don't have to keep track of alts, ips, or deleted users so this should be a lot easier than site-wide bans :P
|
//Thankfully we don't have to keep track of alts, ips, or deleted users so this should be a lot easier than site-wide bans :P
|
||||||
banList: [channelBanSchema]
|
banList: [channelBanSchema]
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,10 @@ const mediaSchema = new mongoose.Schema({
|
||||||
type: mongoose.SchemaTypes.Number,
|
type: mongoose.SchemaTypes.Number,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
});
|
},
|
||||||
|
{
|
||||||
|
discriminatorKey: 'status'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
module.exports = mediaSchema;
|
module.exports = mediaSchema;
|
||||||
|
|
@ -19,16 +19,44 @@ const {mongoose} = require('mongoose');
|
||||||
|
|
||||||
//Local Imports
|
//Local Imports
|
||||||
const mediaSchema = require('./mediaSchema');
|
const mediaSchema = require('./mediaSchema');
|
||||||
|
const queuedMedia = require('../../../app/channel/media/queuedMedia');
|
||||||
|
|
||||||
const queuedProperties = new mongoose.Schema({
|
const queuedProperties = new mongoose.Schema({
|
||||||
startTime: {
|
startTime: {
|
||||||
type: mongoose.SchemaTypes.Number,
|
type: mongoose.SchemaTypes.Number,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
startTimeStamp: {
|
||||||
|
type: mongoose.SchemaTypes.Number,
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
earlyEnd: {
|
||||||
|
type: mongoose.SchemaTypes.Number,
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
uuid: {
|
uuid: {
|
||||||
type: mongoose.SchemaTypes.UUID,
|
type: mongoose.SchemaTypes.UUID,
|
||||||
required: true,
|
required: true,
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
discriminatorKey: 'status'
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = mediaSchema.descriminiator('queued', queuedProperties);
|
//methods
|
||||||
|
queuedProperties.methods.rehydrate = function(){
|
||||||
|
return new queuedMedia(
|
||||||
|
this.title,
|
||||||
|
this.fileName,
|
||||||
|
this.url,
|
||||||
|
this.id,
|
||||||
|
this.type,
|
||||||
|
this.duration,
|
||||||
|
this.startTime,
|
||||||
|
this.startTimeStamp,
|
||||||
|
this.earlyEnd,
|
||||||
|
this.uuid.toString()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = mediaSchema.discriminator('queued', queuedProperties);
|
||||||
|
|
@ -26,12 +26,17 @@ module.exports.errorHandler = function(res, msg, type = "Generic", status = 400)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.exceptionHandler = function(res, err){
|
module.exports.localExceptionHandler = function(err){
|
||||||
//If we're being verbose
|
//If we're being verbose
|
||||||
if(config.verbose){
|
if(config.verbose){
|
||||||
//Log the error
|
//Log the error
|
||||||
console.log(err)
|
console.log(err)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.exceptionHandler = function(res, err){
|
||||||
|
//Locally handle the exception
|
||||||
|
module.exports.localExceptionHandler(err);
|
||||||
|
|
||||||
//if not yell at the browser for fucking up, and tell it what it did wrong.
|
//if not yell at the browser for fucking up, and tell it what it did wrong.
|
||||||
module.exports.errorHandler(res, err.message, "Caught Exception");
|
module.exports.errorHandler(res, err.message, "Caught Exception");
|
||||||
|
|
@ -42,11 +47,8 @@ module.exports.socketErrorHandler = function(socket, msg, type = "Generic"){
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.socketExceptionHandler = function(socket, err){
|
module.exports.socketExceptionHandler = function(socket, err){
|
||||||
//If we're being verbose
|
//Locally handle the exception
|
||||||
if(config.verbose){
|
module.exports.localExceptionHandler(err);
|
||||||
//Log the error
|
|
||||||
console.log(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
//if not yell at the browser for fucking up, and tell it what it did wrong.
|
//if not yell at the browser for fucking up, and tell it what it did wrong.
|
||||||
return module.exports.socketErrorHandler(socket, err.msg, "Caught Exception");
|
return module.exports.socketErrorHandler(socket, err.msg, "Caught Exception");
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. %>
|
||||||
<link rel="stylesheet" type="text/css" href="/css/popup/changePassword.css">
|
<link rel="stylesheet" type="text/css" href="/css/popup/changePassword.css">
|
||||||
<h3 id="password-change-popup-title" class="popup-title">Update Password</h3>
|
<h3 id="password-change-popup-title" class="popup-title">Update Password</h3>
|
||||||
<div id="password-change-popup-content">
|
<div id="password-change-popup-content">
|
||||||
<p id="password-change-popup-caption">Enter new email and current password below:</p>
|
<p id="password-change-popup-caption">Enter new and confirm current password below:</p>
|
||||||
<input type="password" placeholder="password" id="password-change-popup-old-password">
|
<input type="password" placeholder="Current Password" id="password-change-popup-old-password">
|
||||||
<input type="password" placeholder="new password" id="password-change-popup-new-password">
|
<input type="password" placeholder="New Password" id="password-change-popup-new-password">
|
||||||
<input type="password" placeholder="confirm new password" id="password-change-popup-confirm-new-password">
|
<input type="password" placeholder="Confirm New Password" id="password-change-popup-confirm-new-password">
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -80,8 +80,10 @@ class queuePanel extends panelObj{
|
||||||
|
|
||||||
defineListeners(){
|
defineListeners(){
|
||||||
//Render queue when we receive a new copy of the queue data from the server
|
//Render queue when we receive a new copy of the queue data from the server
|
||||||
this.client.socket.on("clientMetadata", (data) => {this.renderQueue();});
|
this.client.socket.on("clientMetadata", () => {this.renderQueue();});
|
||||||
this.client.socket.on("queue", (data) => {this.renderQueue();});
|
this.client.socket.on("queue", () => {this.renderQueue();});
|
||||||
|
this.client.socket.on("start", () => {this.renderQueue();});
|
||||||
|
this.client.socket.on("end", () => {this.renderQueue();});
|
||||||
this.client.socket.on("lock", this.handleScheduleLock.bind(this));
|
this.client.socket.on("lock", this.handleScheduleLock.bind(this));
|
||||||
this.client.socket.on("error", this.handleQueueError.bind(this));
|
this.client.socket.on("error", this.handleQueueError.bind(this));
|
||||||
}
|
}
|
||||||
|
|
@ -415,6 +417,12 @@ class queuePanel extends panelObj{
|
||||||
}
|
}
|
||||||
|
|
||||||
clearQueue(){
|
clearQueue(){
|
||||||
|
//If we have no body
|
||||||
|
if(this.ownerDoc.body == null){
|
||||||
|
//We have bigger issues
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
//Clear out queue container
|
//Clear out queue container
|
||||||
this.queueContainer.innerHTML = '';;
|
this.queueContainer.innerHTML = '';;
|
||||||
//Clear out queue marker container
|
//Clear out queue marker container
|
||||||
|
|
|
||||||
|
|
@ -227,7 +227,7 @@ class accountSettingsButton{
|
||||||
|
|
||||||
class changeEmailPopup{
|
class changeEmailPopup{
|
||||||
constructor(){
|
constructor(){
|
||||||
this.popup = new canopyUXUtils.popup("changeEmail", true, this.asyncConstructor.bind(this), this.asyncConstructor);
|
this.popup = new canopyUXUtils.popup("changeEmail", true, this.asyncConstructor.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
asyncConstructor(){
|
asyncConstructor(){
|
||||||
|
|
@ -251,7 +251,7 @@ class changeEmailPopup{
|
||||||
|
|
||||||
class changePasswordPopup{
|
class changePasswordPopup{
|
||||||
constructor(){
|
constructor(){
|
||||||
this.popup = new canopyUXUtils.popup("changePassword", true, this.asyncConstructor.bind(this), this.asyncConstructor);
|
this.popup = new canopyUXUtils.popup("changePassword", true, this.asyncConstructor.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
asyncConstructor(){
|
asyncConstructor(){
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue