From 44dd613ea355fbcce972d53537a1a9152329ab86 Mon Sep 17 00:00:00 2001 From: rainbow napkin Date: Sun, 9 Feb 2025 00:45:36 -0500 Subject: [PATCH] Added schedule locking and day jumping on scroll lock. --- src/app/channel/connectedUser.js | 5 +- src/app/channel/media/queue.js | 254 ++++++++++-------- .../channel/channelPermissionSchema.js | 18 ++ src/views/partial/panels/queue.ejs | 12 +- www/css/panel/queue.css | 2 +- www/js/channel/channel.js | 11 +- www/js/channel/panels/queuePanel.js | 42 ++- www/js/utils.js | 2 +- 8 files changed, 221 insertions(+), 125 deletions(-) diff --git a/src/app/channel/connectedUser.js b/src/app/channel/connectedUser.js index a7248c9..1b4406a 100644 --- a/src/app/channel/connectedUser.js +++ b/src/app/channel/connectedUser.js @@ -139,8 +139,11 @@ module.exports = class{ //Get schedule as a temporary array const queue = Array.from(this.channel.queue.schedule); + //Get schedule lock status + const queueLock = this.channel.queue.locked; + //Send off the metadata to our user's clients - this.emit("clientMetadata", {user: userObj, flairList, queue}); + this.emit("clientMetadata", {user: userObj, flairList, queue, queueLock}); } async sendSiteEmotes(){ diff --git a/src/app/channel/media/queue.js b/src/app/channel/media/queue.js index 9b859c4..987a722 100644 --- a/src/app/channel/media/queue.js +++ b/src/app/channel/media/queue.js @@ -21,6 +21,7 @@ const validator = require('validator'); const queuedMedia = require('./queuedMedia'); const yanker = require('../../../utils/media/yanker'); const loggerUtils = require('../../../utils/loggerUtils'); +const channelModel = require('../../../schemas/channel/channelSchema'); module.exports = class{ constructor(server, channel){ @@ -43,145 +44,182 @@ module.exports = class{ this.nextTimer = null; //Create variable to hold currently playing media object this.nowPlaying = null; + + //create boolean to hold schedule lock + this.locked = false; } defineListeners(socket){ socket.on("queue", (data) => {this.queueURL(socket, data)}); - socket.on("delete", (data => {this.deleteMedia(socket, data)})); - socket.on("move", (data => {this.moveMedia(socket, data)})); - socket.on("clear", (data => {this.deleteRange(socket, data)})); + socket.on("delete", (data) => {this.deleteMedia(socket, data)}); + socket.on("move", (data) => {this.moveMedia(socket, data)}); + socket.on("clear", (data) => {this.deleteRange(socket, data)}); + socket.on("lock", (data) => {this.toggleLock(socket)}); } - async queueURL(socket, data){ - try{ - //Set url - var url = data.url; + //Get the current channel from the database + const chanDB = await channelModel.findOne({name: socket.chan}); - //If we where given a bad URL - if(!validator.isURL(url)){ - //Attempt to fix the situation by encoding it - url = encodeURI(url); + if((!this.locked && await chanDB.permCheck(socket.user, 'scheduleMedia')) || await chanDB.permCheck(socket.user, 'scheduleAdmin')){ + try{ + //Set url + var url = data.url; - //If it's still bad + //If we where given a bad URL if(!validator.isURL(url)){ + //Attempt to fix the situation by encoding it + url = encodeURI(url); + + //If it's still bad + if(!validator.isURL(url)){ + //Bitch, moan, complain... + loggerUtils.socketErrorHandler(socket, "Bad URL!", "validation"); + //and ignore it! + return; + } + } + + //If the title is too long + if(!validator.isLength(data.title, {max:30})){ //Bitch, moan, complain... - loggerUtils.socketErrorHandler(socket, "Bad URL!", "validation"); + loggerUtils.socketErrorHandler(socket, "Title too long!", "validation"); //and ignore it! return; } - } + + //Set title + const title = validator.escape(validator.trim(data.title)); + //set start + var start = data.start; - //If the title is too long - if(!validator.isLength(data.title, {max:30})){ - //Bitch, moan, complain... - loggerUtils.socketErrorHandler(socket, "Title too long!", "validation"); - //and ignore it! - return; - } - - //Set title - const title = validator.escape(validator.trim(data.title)); - //set start - var start = data.start; - - //If start time isn't an integer after the current epoch - if(start != null &&!validator.isInt(String(start), (new Date().getTime()))){ - //Null out time to tell the later parts of the function to start it now - start = null; - } - - //Pull media list - const mediaList = await yanker.yankMedia(url, title); - - //If we didn't find any media - if(mediaList == null || mediaList.length <= 0){ - //Bitch, moan, complain... - loggerUtils.socketErrorHandler(socket, "No media found!", "queue"); - //and ignore it! - return; - } - - //If we have an invalid time - if(start == null || start < (new Date).getTime()){ - //Get last item from schedule - const lastItem = (Array.from(this.schedule)[this.schedule.size - 1]); - - //if we have a last item - if(lastItem != null){ - //Throw it on five ms after the last item - start = lastItem[1].startTime + (lastItem[1].duration * 1000) + 5; + //If start time isn't an integer after the current epoch + if(start != null &&!validator.isInt(String(start), (new Date().getTime()))){ + //Null out time to tell the later parts of the function to start it now + start = null; } - } - //Queue the first media object given - this.queueMedia(mediaList[0], start, socket); - }catch(err){ - return loggerUtils.socketExceptionHandler(socket, err); + //Pull media list + const mediaList = await yanker.yankMedia(url, title); + + //If we didn't find any media + if(mediaList == null || mediaList.length <= 0){ + //Bitch, moan, complain... + loggerUtils.socketErrorHandler(socket, "No media found!", "queue"); + //and ignore it! + return; + } + + //If we have an invalid time + if(start == null || start < (new Date).getTime()){ + //Get last item from schedule + const lastItem = (Array.from(this.schedule)[this.schedule.size - 1]); + + //if we have a last item + if(lastItem != null){ + //Throw it on five ms after the last item + start = lastItem[1].startTime + (lastItem[1].duration * 1000) + 5; + } + } + + //Queue the first media object given + this.queueMedia(mediaList[0], start, socket); + }catch(err){ + return loggerUtils.socketExceptionHandler(socket, err); + } } } - deleteRange(socket, data){ - try{ - //If start time isn't an integer - if(data.start != null && !validator.isInt(String(data.start))){ - //Bitch, moan, complain... - loggerUtils.socketErrorHandler(socket, "Bad start date!", "queue"); - //and ignore it! - return; - } + async deleteRange(socket, data){ + //Get the current channel from the database + const chanDB = await channelModel.findOne({name: socket.chan}); - //If end time isn't an integer - if(data.end != null && !validator.isInt(String(data.end))){ - //Bitch, moan, complain... - loggerUtils.socketErrorHandler(socket, "Bad end date!", "queue"); - //and ignore it! - return; - } + if((!this.locked && await chanDB.permCheck(socket.user, 'clearSchedule')) || await chanDB.permCheck(socket.user, 'scheduleAdmin')){ + try{ + //If start time isn't an integer + if(data.start != null && !validator.isInt(String(data.start))){ + //Bitch, moan, complain... + loggerUtils.socketErrorHandler(socket, "Bad start date!", "queue"); + //and ignore it! + return; + } - this.removeRange(data.start, data.end, socket); - }catch(err){ - return loggerUtils.socketExceptionHandler(socket, err); + //If end time isn't an integer + if(data.end != null && !validator.isInt(String(data.end))){ + //Bitch, moan, complain... + loggerUtils.socketErrorHandler(socket, "Bad end date!", "queue"); + //and ignore it! + return; + } + + this.removeRange(data.start, data.end, socket); + }catch(err){ + return loggerUtils.socketExceptionHandler(socket, err); + } } } - deleteMedia(socket, data){ - try{ - //If we don't have a valid UUID - if(!validator.isUUID(data.uuid)){ - //Bitch, moan, complain... - loggerUtils.socketErrorHandler(socket, "Bad UUID!", "validation"); - //and ignore it! - return; - } + async deleteMedia(socket, data){ + //Get the current channel from the database + const chanDB = await channelModel.findOne({name: socket.chan}); - //Remove media by UUID - this.removeMedia(data.uuid, socket); - }catch(err){ - return loggerUtils.socketExceptionHandler(socket, err); + if((!this.locked && await chanDB.permCheck(socket.user, 'scheduleMedia')) || await chanDB.permCheck(socket.user, 'scheduleAdmin')){ + try{ + //If we don't have a valid UUID + if(!validator.isUUID(data.uuid)){ + //Bitch, moan, complain... + loggerUtils.socketErrorHandler(socket, "Bad UUID!", "validation"); + //and ignore it! + return; + } + + //Remove media by UUID + this.removeMedia(data.uuid, socket); + }catch(err){ + return loggerUtils.socketExceptionHandler(socket, err); + } } } - moveMedia(socket, data){ - try{ - //If we don't have a valid UUID - if(!validator.isUUID(data.uuid)){ - //Bitch, moan, complain... - loggerUtils.socketErrorHandler(socket, "Bad UUID!", "validation"); - //and ignore it! - return; - } + async moveMedia(socket, data){ + //Get the current channel from the database + const chanDB = await channelModel.findOne({name: socket.chan}); - //If start time isn't an integer after the current epoch - if(data.start != null && !validator.isInt(String(data.start))){ - //Null out time to tell the later parts of the function to start it now - data.start = undefined; - } + if((!this.locked && await chanDB.permCheck(socket.user, 'scheduleMedia')) || await chanDB.permCheck(socket.user, 'scheduleAdmin')){ + try{ + //If we don't have a valid UUID + if(!validator.isUUID(data.uuid)){ + //Bitch, moan, complain... + loggerUtils.socketErrorHandler(socket, "Bad UUID!", "validation"); + //and ignore it! + return; + } - //Move media by UUID - this.rescheduleMedia(data.uuid, data.start, socket); - }catch(err){ - return loggerUtils.socketExceptionHandler(socket, err); + //If start time isn't an integer after the current epoch + if(data.start != null && !validator.isInt(String(data.start))){ + //Null out time to tell the later parts of the function to start it now + data.start = undefined; + } + + //Move media by UUID + this.rescheduleMedia(data.uuid, data.start, socket); + }catch(err){ + return loggerUtils.socketExceptionHandler(socket, err); + } + } + } + + async toggleLock(socket){ + //Get the current channel from the database + const chanDB = await channelModel.findOne({name: socket.chan}); + + //If the user is a schedule admin + if(await chanDB.permCheck(socket.user, 'scheduleAdmin')){ + //Toggle the schedule lock + this.locked = !this.locked; + + //Update schedule lock status for everyone in the channel + this.server.io.in(this.channel.name).emit("lock", {locked: this.locked}); } } diff --git a/src/schemas/channel/channelPermissionSchema.js b/src/schemas/channel/channelPermissionSchema.js index 1185fa1..561f982 100644 --- a/src/schemas/channel/channelPermissionSchema.js +++ b/src/schemas/channel/channelPermissionSchema.js @@ -82,6 +82,24 @@ const channelPermissionSchema = new mongoose.Schema({ default: "admin", required: true }, + scheduleMedia: { + type: mongoose.SchemaTypes.String, + enum: rankEnum, + default: "admin", + required: true + }, + clearSchedule:{ + type: mongoose.SchemaTypes.String, + enum: rankEnum, + default: "admin", + required: true + }, + scheduleAdmin:{ + type: mongoose.SchemaTypes.String, + enum: rankEnum, + default: "admin", + required: true + }, deleteChannel: { type: mongoose.SchemaTypes.String, enum: rankEnum, diff --git a/src/views/partial/panels/queue.ejs b/src/views/partial/panels/queue.ejs index 470ac2b..5b5b7d3 100644 --- a/src/views/partial/panels/queue.ejs +++ b/src/views/partial/panels/queue.ejs @@ -17,13 +17,13 @@ along with this program. If not, see . %>
- - + + - - - - + + + +