From 7530b5fa5fdf574193bfe3db20680bd5b9e37425 Mon Sep 17 00:00:00 2001 From: rainbow napkin Date: Tue, 23 Jun 2026 03:56:33 -0400 Subject: [PATCH] Re-worked playlistHandler.js for site-wide playlist collections. --- src/app/channel/media/playlistHandler.js | 313 +++++++++++------- .../panels/queuePanel/playlistManager.js | 11 +- 2 files changed, 210 insertions(+), 114 deletions(-) diff --git a/src/app/channel/media/playlistHandler.js b/src/app/channel/media/playlistHandler.js index ebe667e..22d6c27 100644 --- a/src/app/channel/media/playlistHandler.js +++ b/src/app/channel/media/playlistHandler.js @@ -79,12 +79,12 @@ class playlistHandler{ /** * Validates client requests involving playlist names * @param {Socket} socket - Newly connected socket to define listeners against - * @param {Object} data - Data handed over from the client + * @param {Object} playlist - String containing raw playlist title * @returns {String} returns escaped/trimmed name upon success */ - playlistTitleValidator(socket, data){ + playlistTitleValidator(socket, playlist){ //If the title is too long - if(typeof data.playlist != 'string' || !validator.isLength(data.playlist, {min: 1, max:30})){ + if(typeof playlist != 'string' || !validator.isLength(playlist, {min: 1, max:30})){ //Bitch, moan, complain... loggerUtils.socketErrorHandler(socket, "Invalid Playlist Name!", "validation"); //and ignore it! @@ -92,7 +92,7 @@ class playlistHandler{ } //Escape/trim the playlist name - return validator.escape(validator.trim(data.playlist)); + return validator.escape(validator.trim(playlist)); } //Validation/Sanatization functions @@ -107,7 +107,7 @@ class playlistHandler{ const safeTitles = []; //Validate title - const playlist = this.playlistTitleValidator(socket, data); + const playlist = this.playlistTitleValidator(socket, data.playlist); //If title is bad if(playlist == null){ @@ -242,7 +242,17 @@ class playlistHandler{ */ async getChannelPlaylists(socket){ try{ - const playlists = await channelPlaylistModel.find({channel: this.channel._id}); + //pull all of the channels playlists + const foundPlaylists = await channelPlaylistModel.find({channel: this.channel._id}); + + //Create a new array to hold dehydated playlists + const playlists = []; + + //For each found playlist + for(let playlist of foundPlaylists){ + //Dehydrate it and put it in our lists of playlist to send to the user + playlists.push(playlist.dehydrate()); + } //Return playlists socket.emit('chanPlaylists', playlists); @@ -257,7 +267,17 @@ class playlistHandler{ */ async getUserPlaylists(socket){ try{ - const playlists = await userPlaylistModel.find({user: socket.request.session.user._id}); + //pull all of users playlists + const foundPlaylists = await userPlaylistModel.find({user: socket.request.session.user._id}); + + //Create a new array to hold dehydated playlists + const playlists = []; + + //For each found playlist + for(let playlist of foundPlaylists){ + //Dehydrate it and put it in our lists of playlist to send to the user + playlists.push(playlist.dehydrate()); + } //Return playlists socket.emit('userPlaylists', playlists); @@ -372,7 +392,7 @@ class playlistHandler{ try{ //Validate playlist name - const playlist = this.playlistTitleValidator(socket, data); + const playlist = this.playlistTitleValidator(socket, data.playlist); //If playlist title isnt valid if(playlist == null){ @@ -416,7 +436,7 @@ class playlistHandler{ try{ //Validate playlist name - const playlist = this.playlistTitleValidator(socket, data); + const playlist = this.playlistTitleValidator(socket, data.playlist); //If playlist title isnt valid if(playlist == null){ @@ -454,7 +474,7 @@ class playlistHandler{ async addToChannelPlaylist(socket, data, chanDB){ try{ //Validate playlist name - const playlist = this.playlistTitleValidator(socket, data); + const playlist = this.playlistTitleValidator(socket, data.playlist); //If playlist title isnt valid if(playlist == null){ @@ -512,7 +532,7 @@ class playlistHandler{ async addToUserPlaylist(socket, data){ try{ //Validate playlist name - const playlist = this.playlistTitleValidator(socket, data); + const playlist = this.playlistTitleValidator(socket, data.playlist); //syntatic suger XP const userID = socket.request.session.user._id; @@ -565,7 +585,7 @@ class playlistHandler{ async queueChannelPlaylist(socket, data, chanDB){ try{ //Validate playlist name - const playlist = this.playlistTitleValidator(socket, data); + const playlist = this.playlistTitleValidator(socket, data.playlist); //If playlist title isnt valid if(playlist == null){ @@ -622,14 +642,13 @@ class playlistHandler{ * Queues an entire user playlist * @param {Socket} socket - Requesting socket * @param {Object} data - Data handed over from the client - * @param {Mongoose.Document} userDB - User Document Passthrough to save on DB Access * @param {Mongoose.Document} chanDB - Channel Document Passthrough to save on DB Access */ - async queueUserPlaylist(socket, data, userDB, chanDB){ + async queueUserPlaylist(socket, data, chanDB){ try{ //Validate playlist name - const playlist = this.playlistTitleValidator(socket, data); + const playlist = this.playlistTitleValidator(socket, data.playlist); //syntatic suger XP const userID = socket.request.session.user._id; @@ -639,12 +658,6 @@ class playlistHandler{ return; } - //if we wherent handed a user document - if(userDB == null){ - //Find the user in the Database - userDB = await userModel.findOne({user: socket.request.session.user.user}); - } - //if we wherent handed a channel document if(chanDB == null){ //Pull it based on channel name @@ -707,6 +720,14 @@ class playlistHandler{ return; } + //Validate playlist name + const playlist = this.playlistTitleValidator(socket, data.playlist); + + //If playlist title isnt valid + if(playlist == null){ + return; + } + //if we wherent handed a channel document if(chanDB == null){ //Pull it based on channel name @@ -715,22 +736,22 @@ class playlistHandler{ //Permcheck the user if((!this.channel.queue.locked && await chanDB.permCheck(socket.user, 'scheduleMedia')) || await chanDB.permCheck(socket.user, 'scheduleAdmin')){ - //Grab playlist - const playlist = chanDB.getPlaylistByName(data.playlist); + //Check for existing playlists of the same name + const foundPlaylist = await channelPlaylistModel.findOne({channel: this.channel._id, name: playlist}); //If we didn't find a real playlist - if(playlist == null){ + if(foundPlaylist == null){ //Bitch, moan, complain... loggerUtils.socketErrorHandler(socket, "Playlist not found!", "validation"); //and ignore it! return; - } + } //Pull and rehydrate media from playlist - const media = playlist.findMediaByUUID(data.uuid).rehydrate(); + const media = foundPlaylist.findMediaByUUID(data.uuid).rehydrate(); //Set title from default titles - media.title = playlist.pickDefaultTitle(media.title); + media.title = foundPlaylist.pickDefaultTitle(media.title); //Queue found media this.channel.queue.scheduleMedia(queuedMedia.fromMediaArray([media], start), socket, chanDB); @@ -744,10 +765,9 @@ class playlistHandler{ * Queues media from a given user playlist * @param {Socket} socket - Requesting socket * @param {Object} data - Data handed over from the client - * @param {Mongoose.Document} userDB - User Document Passthrough to save on DB Access * @param {Mongoose.Document} chanDB - Channel Document Passthrough to save on DB Access */ - async queueFromUserPlaylist(socket, data, userDB, chanDB){ + async queueFromUserPlaylist(socket, data, chanDB){ try{ //Validate data const start = this.queueFromChannelPlaylistValidator(socket, data); @@ -758,10 +778,15 @@ class playlistHandler{ return; } - //if we wherent handed a user document - if(userDB == null){ - //Find the user in the Database - userDB = await userModel.findOne({user: socket.request.session.user.user}); + //Validate playlist name + const playlist = this.playlistTitleValidator(socket, data.playlist); + + //syntatic suger XP + const userID = socket.request.session.user._id; + + //If playlist title isnt valid + if(playlist == null){ + return; } //if we wherent handed a channel document @@ -772,22 +797,22 @@ class playlistHandler{ //Permcheck the user if((!this.channel.queue.locked && await chanDB.permCheck(socket.user, 'scheduleMedia')) || await chanDB.permCheck(socket.user, 'scheduleAdmin')){ - //Grab playlist - const playlist = userDB.getPlaylistByName(data.playlist); + //Check for existing playlists of the same name + const foundPlaylist = await userPlaylistModel.findOne({user: userID, name: playlist}); - //If we didn't find a real playlist - if(playlist == null){ + //If the channel doesn't exist + if(foundPlaylist == null){ //Bitch, moan, complain... - loggerUtils.socketErrorHandler(socket, "Playlist not found!", "validation"); + loggerUtils.socketErrorHandler(socket, `Playlist not found!`, "validation"); //and ignore it! return; - } + } //Pull and rehydrate media from playlist - const media = playlist.findMediaByUUID(data.uuid).rehydrate(); + const media = foundPlaylist.findMediaByUUID(data.uuid).rehydrate(); //Set title from default titles - media.title = playlist.pickDefaultTitle(media.title); + media.title = foundPlaylist.pickDefaultTitle(media.title); //Queue found media this.channel.queue.scheduleMedia(queuedMedia.fromMediaArray([media], start), socket, chanDB); @@ -805,6 +830,14 @@ class playlistHandler{ */ async queueRandomFromChannelPlaylist(socket, data, chanDB){ try{ + //Validate playlist name + const playlist = this.playlistTitleValidator(socket, data.playlist); + + //If playlist title isnt valid + if(playlist == null){ + return; + } + //if we wherent handed a channel document if(chanDB == null){ //Pull it based on channel name @@ -816,11 +849,11 @@ class playlistHandler{ //Pull a valid start time from input, or make one up if we can't let start = this.channel.queue.getStart(data.start); - //Grab playlist - const playlist = chanDB.getPlaylistByName(data.playlist); + //Check for existing playlists of the same name + const foundPlaylist = await channelPlaylistModel.findOne({channel: this.channel._id, name: playlist}); //If we didn't find a real playlist - if(playlist == null){ + if(foundPlaylist == null){ //Bitch, moan, complain... loggerUtils.socketErrorHandler(socket, "Playlist not found!", "validation"); //and ignore it! @@ -828,13 +861,13 @@ class playlistHandler{ } //Pick a random video ID based on the playlist's length - const foundID = Math.round(Math.random() * (playlist.media.length - 1)); + const foundID = Math.round(Math.random() * (foundPlaylist.media.length - 1)); //Pull and rehydrate media from playlist - const foundMedia = playlist.media[foundID].rehydrate(); + const foundMedia = foundPlaylist.media[foundID].rehydrate(); //Set title from default titles - foundMedia.title = playlist.pickDefaultTitle(foundMedia.title); + foundMedia.title = foundPlaylist.pickDefaultTitle(foundMedia.title); //Queue found media this.channel.queue.scheduleMedia(queuedMedia.fromMediaArray([foundMedia], start), socket, chanDB); @@ -848,47 +881,52 @@ class playlistHandler{ * Queues random media from a given user playlist * @param {Socket} socket - Requesting socket * @param {Object} data - Data handed over from the client - * @param {Mongoose.Document} userDB - User Document Passthrough to save on DB Access * @param {Mongoose.Document} chanDB - Channel Document Passthrough to save on DB Access */ - async queueRandomFromUserPlaylist(socket, data, userDB, chanDB){ + async queueRandomFromUserPlaylist(socket, data, chanDB){ try{ + + //Validate playlist name + const playlist = this.playlistTitleValidator(socket, data.playlist); + + //syntatic suger XP + const userID = socket.request.session.user._id; + + //If playlist title isnt valid + if(playlist == null){ + return; + } + //if we wherent handed a channel document if(chanDB == null){ //Pull it based on channel name chanDB = await channelModel.findOne({name: this.channel.name}); } - //if we wherent handed a user document - if(userDB == null){ - //Find the user in the Database - userDB = await userModel.findOne({user: socket.request.session.user.user}); - } - //Permcheck the user if((!this.channel.queue.locked && await chanDB.permCheck(socket.user, 'scheduleMedia')) || await chanDB.permCheck(socket.user, 'scheduleAdmin')){ //Pull a valid start time from input, or make one up if we can't let start = this.channel.queue.getStart(data.start); - //Grab playlist - const playlist = userDB.getPlaylistByName(data.playlist); + //Check for existing playlists of the same name + const foundPlaylist = await userPlaylistModel.findOne({user: userID, name: playlist}); - //If we didn't find a real playlist - if(playlist == null){ + //If the channel doesn't exist + if(foundPlaylist == null){ //Bitch, moan, complain... - loggerUtils.socketErrorHandler(socket, "Playlist not found!", "validation"); + loggerUtils.socketErrorHandler(socket, `Playlist not found!`, "validation"); //and ignore it! return; } //Pick a random video ID based on the playlist's length - const foundID = Math.round(Math.random() * (playlist.media.length - 1)); + const foundID = Math.round(Math.random() * (foundPlaylist.media.length - 1)); //Pull and rehydrate media from playlist - const foundMedia = playlist.media[foundID].rehydrate(); + const foundMedia = foundPlaylist.media[foundID].rehydrate(); //Set title from default titles - foundMedia.title = playlist.pickDefaultTitle(foundMedia.title); + foundMedia.title = foundPlaylist.pickDefaultTitle(foundMedia.title); //Queue found media this.channel.queue.scheduleMedia(queuedMedia.fromMediaArray([foundMedia], start), socket, chanDB); @@ -907,8 +945,16 @@ class playlistHandler{ */ async renameChannelPlaylist(socket, data, chanDB){ try{ + //Validate playlist name + const playlist = this.playlistTitleValidator(socket, data.playlist); + + //If playlist title isnt valid + if(playlist == null){ + return; + } + //Validate and Sanatize name - const name = this.playlistTitleValidator(socket, data); + const name = this.playlistTitleValidator(socket, data.name); //If validation fucked up if(name == null){ @@ -923,19 +969,22 @@ class playlistHandler{ } if(await chanDB.permCheck(socket.user, 'editChannelPlaylists')){ + //Check for playlist to be re-named + const existingPlaylist = await channelPlaylistModel.findOne({channel: this.channel._id, name}); + //If the new name already exists - if(chanDB.getPlaylistByName(name) != null){ + if(existingPlaylist != null){ //Bitch, moan, complain... loggerUtils.socketErrorHandler(socket, `Playlist named '${name}' already exists!`, "validation"); //and ignore it! return; } - //Find playlist - let playlist = chanDB.getPlaylistByName(data.playlist); + //Check for playlist to be re-named + const foundPlaylist = await channelPlaylistModel.findOne({channel: this.channel._id, name: playlist}); //If we didn't find a real playlist - if(playlist == null){ + if(foundPlaylist == null){ //Bitch, moan, complain... loggerUtils.socketErrorHandler(socket, "Playlist not found!", "validation"); //and ignore it! @@ -943,10 +992,10 @@ class playlistHandler{ } //Change playlist name - chanDB.media.playlists[playlist.listIndex].name = name; + foundPlaylist.name = name; //Save channel document - await chanDB.save(); + await foundPlaylist.save(); //Return playlists from channel doc this.getChannelPlaylists(socket); @@ -960,12 +1009,23 @@ class playlistHandler{ * Renames a user playlist * @param {Socket} socket - Requesting socket * @param {Object} data - Data handed over from the client - * @param {Mongoose.Document} userDB - User Document Passthrough to save on DB Access */ - async renameUserPlaylist(socket, data, userDB){ + async renameUserPlaylist(socket, data){ try{ + + //Validate playlist name + const playlist = this.playlistTitleValidator(socket, data.playlist); + + //syntatic suger XP + const userID = socket.request.session.user._id; + + //If playlist title isnt valid + if(playlist == null){ + return; + } + //Validate and Sanatize name - const name = this.playlistTitleValidator(socket, data); + const name = this.playlistTitleValidator(socket, data.name); //If validation fucked up if(name == null){ @@ -973,36 +1033,33 @@ class playlistHandler{ return; } - //if we wherent handed a user document - if(userDB == null){ - //Find the user in the Database - userDB = await userModel.findOne({user: socket.request.session.user.user}); - } + //Check for existing playlists of the same name + const existingPlaylist = await userPlaylistModel.findOne({user: userID, name}); - //If the new name already exists - if(userDB.getPlaylistByName(name) != null){ + //If the channel doesn't exist + if(existingPlaylist != null){ //Bitch, moan, complain... loggerUtils.socketErrorHandler(socket, `Playlist named '${name}' already exists!`, "validation"); //and ignore it! return; } - //Find playlist - let playlist = userDB.getPlaylistByName(data.playlist); + //Check for existing playlists of the same name + const foundPlaylist = await userPlaylistModel.findOne({user: userID, name: playlist}); - //If we didn't find a real playlist - if(playlist == null){ + //If the channel doesn't exist + if(foundPlaylist == null){ //Bitch, moan, complain... - loggerUtils.socketErrorHandler(socket, "Playlist not found!", "validation"); + loggerUtils.socketErrorHandler(socket, `Playlist not found!`, "validation"); //and ignore it! return; } //Change playlist name - userDB.playlists[playlist.listIndex].name = name; + foundPlaylist.name = name; //Save channel document - await userDB.save(); + await foundPlaylist.save(); //Return playlists from channel doc this.getUserPlaylists(socket); @@ -1020,6 +1077,14 @@ class playlistHandler{ */ async changeDefaultTitlesChannelPlaylist(socket, data, chanDB){ try{ + //Validate playlist name + const playlist = this.playlistTitleValidator(socket, data.playlist); + + //If playlist title isnt valid + if(playlist == null){ + return; + } + //if we wherent handed a channel document if(chanDB == null){ //Pull it based on channel name @@ -1027,11 +1092,11 @@ class playlistHandler{ } if(await chanDB.permCheck(socket.user, 'editChannelPlaylists')){ - //Find playlist - let playlist = chanDB.getPlaylistByName(data.playlist); + //Check for existing playlists of the same name + const foundPlaylist = await channelPlaylistModel.findOne({channel: this.channel._id, name: playlist}); //If we didn't find a real playlist - if(playlist == null){ + if(foundPlaylist == null){ //Bitch, moan, complain... loggerUtils.socketErrorHandler(socket, "Playlist not found!", "validation"); //and ignore it! @@ -1039,10 +1104,10 @@ class playlistHandler{ } //Keep valid default titles - chanDB.media.playlists[playlist.listIndex].defaultTitles = this.changeDefaultTitlesValidator(data); + foundPlaylist.defaultTitles = this.changeDefaultTitlesValidator(data); //Save channel document - await chanDB.save(); + await foundPlaylist.save(); //Return playlists from channel doc this.getChannelPlaylists(socket); @@ -1060,29 +1125,34 @@ class playlistHandler{ */ async changeDefaultTitlesUserPlaylist(socket, data, userDB){ try{ + //Validate playlist name + const playlist = this.playlistTitleValidator(socket, data.playlist); + + //syntatic suger XP + const userID = socket.request.session.user._id; + //if we wherent handed a user document if(userDB == null){ //Find the user in the Database userDB = await userModel.findOne({user: socket.request.session.user.user}); } - //Find playlist - let playlist = userDB.getPlaylistByName(data.playlist); + //Check for existing playlists of the same name + const foundPlaylist = await userPlaylistModel.findOne({user: userID, name: playlist}); - //If we didn't find a real playlist - if(playlist == null){ + //If the channel doesn't exist + if(foundPlaylist == null){ //Bitch, moan, complain... - loggerUtils.socketErrorHandler(socket, "Playlist not found!", "validation"); + loggerUtils.socketErrorHandler(socket, `Playlist not found!`, "validation"); //and ignore it! return; } - //Keep valid de.mediafault titles - userDB.playlists[playlist.listIndex].defaultTitles = this.changeDefaultTitlesValidator(data); + foundPlaylist.defaultTitles = this.changeDefaultTitlesValidator(data); //Save user document - await userDB.save(); + await foundPlaylist.save(); //Return playlists from user doc this.getUserPlaylists(socket); @@ -1100,6 +1170,14 @@ class playlistHandler{ */ async deleteChannelPlaylistMedia(socket, data, chanDB){ try{ + //Validate playlist name + const playlist = this.playlistTitleValidator(socket, data.playlist); + + //If playlist title isnt valid + if(playlist == null){ + return; + } + //Validate UUID const uuid = this.deletePlaylistMediaValidator(socket, data); @@ -1116,11 +1194,11 @@ class playlistHandler{ } if(await chanDB.permCheck(socket.user, 'editChannelPlaylists')){ - //Find the playlist - let playlist = chanDB.getPlaylistByName(data.playlist); + //Check for existing playlists of the same name + const foundPlaylist = await channelPlaylistModel.findOne({channel: this.channel._id, name: playlist}); //If we didn't find a real playlist - if(playlist == null){ + if(foundPlaylist == null){ //Bitch, moan, complain... loggerUtils.socketErrorHandler(socket, "Playlist not found!", "validation"); //and ignore it! @@ -1128,10 +1206,10 @@ class playlistHandler{ } //delete media from playlist - chanDB.media.playlists[playlist.listIndex].deleteMedia(data.uuid); + foundPlaylist.deleteMedia(data.uuid); //save the channel document - await chanDB.save(); + await foundPlaylist.save(); //Return playlists from channel doc this.getChannelPlaylists(socket); @@ -1149,6 +1227,17 @@ class playlistHandler{ */ async deleteUserPlaylistMedia(socket, data, userDB){ try{ + //Validate playlist name + const playlist = this.playlistTitleValidator(socket, data.playlist); + + //syntatic suger XP + const userID = socket.request.session.user._id; + + //If playlist title isnt valid + if(playlist == null){ + return; + } + //Validate UUID const uuid = this.deletePlaylistMediaValidator(socket, data); @@ -1164,22 +1253,22 @@ class playlistHandler{ userDB = await userModel.findOne({user: socket.request.session.user.user}); } - //Find the playlist - let playlist = userDB.getPlaylistByName(data.playlist); + //Check for existing playlists of the same name + const foundPlaylist = await userPlaylistModel.findOne({user: userID, name: playlist}); - //If we didn't find a real playlist - if(playlist == null){ + //If the channel doesn't exist + if(foundPlaylist == null){ //Bitch, moan, complain... - loggerUtils.socketErrorHandler(socket, "Playlist not found!", "validation"); + loggerUtils.socketErrorHandler(socket, `Playlist not found!`, "validation"); //and ignore it! return; } //delete media from playlist - userDB.playlists[playlist.listIndex].deleteMedia(data.uuid); + foundPlaylist.deleteMedia(data.uuid); //save the user document - await userDB.save(); + await foundPlaylist.save(); //Return playlists from user doc this.getUserPlaylists(socket); diff --git a/www/js/channel/panels/queuePanel/playlistManager.js b/www/js/channel/panels/queuePanel/playlistManager.js index 57e4022..cf1de3e 100644 --- a/www/js/channel/panels/queuePanel/playlistManager.js +++ b/www/js/channel/panels/queuePanel/playlistManager.js @@ -580,6 +580,7 @@ class playlistManager{ event.target.parentNode.dataset['playlist'], this.client, this.queuePanel.ownerDoc, + event.target.parentNode.dataset['location'], handleOpenedMedia.bind(this) ); @@ -870,9 +871,10 @@ class renamePopup{ * @param {String} playlist - Playlist name * @param {channel} client - Parent Client Management Object * @param {Document} doc - Current owner documnet of the panel, so we know where to drop our pop-up + * @param {String} location - Location of playlist, either Channel or User * @param {Function} cb - Callback function, passed new name upon rename */ - constructor(event, playlist, client, doc, cb){ + constructor(event, playlist, client, doc, location, cb){ /** * Parent Client Management Object */ @@ -888,6 +890,11 @@ class renamePopup{ */ this.cb = cb; + /** + * Location of playlist, either Channel or User + */ + this.location = location; + //Create media popup and call async constructor when done //unfortunately we cant call constructors asyncronously, and we cant call back to this from super, so we can't extend this as it stands :( /** @@ -924,7 +931,7 @@ class renamePopup{ if(event.key == null || event.key == "Enter"){ //Tell the server to change the titles - this.client.socket.emit('renameChannelPlaylist', { + this.client.socket.emit(`rename${this.location}Playlist`, { playlist: this.playlist, name: this.renamePrompt.value });