diff --git a/src/app/channel/media/playlistHandler.js b/src/app/channel/media/playlistHandler.js index 3ad64c6..c2c6bfb 100644 --- a/src/app/channel/media/playlistHandler.js +++ b/src/app/channel/media/playlistHandler.js @@ -22,6 +22,7 @@ const queuedMedia = require('./queuedMedia'); const loggerUtils = require('../../../utils/loggerUtils'); const yanker = require('../../../utils/media/yanker'); const channelModel = require('../../../schemas/channel/channelSchema'); +const { userModel } = require('../../../schemas/user/userSchema'); module.exports = class{ constructor(server, chanDB, channel){ @@ -32,6 +33,7 @@ module.exports = class{ } defineListeners(socket){ + //Channel Playlist Listeners socket.on("getChannelPlaylists", () => {this.getChannelPlaylists(socket)}); socket.on("createChannelPlaylist", (data) => {this.createChannelPlaylist(socket, data)}); socket.on("deleteChannelPlaylist", (data) => {this.deleteChannelPlaylist(socket, data)}); @@ -42,9 +44,14 @@ module.exports = class{ socket.on("queueFromChannelPlaylist", (data) => {this.queueFromChannelPlaylist(socket, data)}); socket.on("renameChannelPlaylist", (data) => {this.renameChannelPlaylist(socket, data)}); socket.on("changeDefaultTitlesChannelPlaylist", (data) => {this.changeDefaultTitlesChannelPlaylist(socket, data)}); + + //User Playlist Listeners + socket.on("getUserPlaylists", () => {this.getUserPlaylists(socket)}); + socket.on("createUserPlaylist", (data) => {this.createUserPlaylist(socket, data)}); + socket.on("deleteUserPlaylist", (data) => {this.deleteUserPlaylist(socket, data)}); } - //--- USER-FACING PLAYLIST FUNCTIONS --- + //Get playlist functions async getChannelPlaylists(socket, chanDB){ try{ //if we wherent handed a channel document @@ -60,6 +67,52 @@ module.exports = class{ } } + async getUserPlaylists(socket, userDB){ + try{ + //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}); + } + + //Return playlists + socket.emit('userPlaylists', userDB.getPlaylists()); + }catch(err){ + return loggerUtils.socketExceptionHandler(socket, err); + } + } + + //Create playlist functions + createPlaylistValidator(socket, data){ + //Create empty array to hold titles + const safeTitles = []; + + //If the title is too long + if(!validator.isLength(data.playlist, {min: 1, max:30})){ + //Bitch, moan, complain... + loggerUtils.socketErrorHandler(socket, "Playlist name too long!", "validation"); + //and ignore it! + return; + } + + if(data.defaultTitles != null){ + //For each default title passed by the data + for(let title of data.defaultTitles){ + //If the title isn't too long + if(validator.isLength(title, {min:1, max:30})){ + //Add it to the safe title list + safeTitles.push(validator.escape(validator.trim(title))) + } + } + } + + //Escape/trim the playlist name + return { + playlist: validator.escape(validator.trim(data.playlist)), + defaultTitles: safeTitles + } + } + async createChannelPlaylist(socket, data, chanDB){ try{ //if we wherent handed a channel document @@ -69,41 +122,27 @@ module.exports = class{ } if(await chanDB.permCheck(socket.user, 'editChannelPlaylists')){ - //If the title is too long - if(!validator.isLength(data.playlist, {max:30})){ - //Bitch, moan, complain... - loggerUtils.socketErrorHandler(socket, "Playlist name too long!", "validation"); - //and ignore it! + //Validate Data + const validData = this.createPlaylistValidator(socket, data); + + //If we got bad data + if(validData == null){ + //Do nothing return; } - //Escape/trim the playlist name - const name = validator.escape(validator.trim(data.playlist)); - //If the channel already exists - if(chanDB.getPlaylistByName(name) != null){ + if(chanDB.getPlaylistByName(validData.playlist) != null){ //Bitch, moan, complain... - loggerUtils.socketErrorHandler(socket, `Playlist named '${name}' already exists!`, "validation"); + loggerUtils.socketErrorHandler(socket, `Playlist named '${validData.playlist}' already exists!`, "validation"); //and ignore it! return; - } - - //Create empty array to hold titles - const safeTitles = []; - - //For each default title passed by the data - for(let title of data.defaultTitles){ - //If the title isn't too long - if(validator.isLength(title, {min:1, max:30})){ - //Add it to the safe title list - safeTitles.push(validator.escape(validator.trim(title))) - } - } + } //Add playlist to the channel doc chanDB.media.playlists.push({ - name, - defaultTitles: safeTitles + name: validData.playlist, + defaultTitles: validData.defaultTitles }); //Save the channel doc @@ -117,6 +156,48 @@ module.exports = class{ } } + async createUserPlaylist(socket, data, userDB){ + try{ + //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 Data + const validData = this.createPlaylistValidator(socket, data); + + //If we got bad data + if(validData == null){ + //Do nothing + return; + } + + //If the channel already exists + if(userDB.getPlaylistByName(validData.playlist) != null){ + //Bitch, moan, complain... + loggerUtils.socketErrorHandler(socket, `Playlist named '${validData.playlist}' already exists!`, "validation"); + //and ignore it! + return; + } + + //Add playlist to the channel doc + userDB.playlists.push({ + name: validData.playlist, + defaultTitles: validData.defaultTitles + }); + + //Save the channel doc + await userDB.save(); + + //Return playlists from channel doc + this.getUserPlaylists(socket, userDB); + }catch(err){ + return loggerUtils.socketExceptionHandler(socket, err); + } + } + + //Delete playlist functions async deleteChannelPlaylist(socket, data, chanDB){ try{ //if we wherent handed a channel document @@ -125,6 +206,14 @@ module.exports = class{ chanDB = await channelModel.findOne({name: this.channel.name}); } + //If the channel doesn't exist + if(chanDB.getPlaylistByName(data.playlist) == null){ + //Bitch, moan, complain... + loggerUtils.socketErrorHandler(socket, `Playlist named '${data.playlist}' doesn't exist!`, "validation"); + //and ignore it! + return; + } + if(await chanDB.permCheck(socket.user, 'editChannelPlaylists')){ //Delete playlist name await chanDB.deletePlaylistByName(data.playlist); @@ -137,6 +226,33 @@ module.exports = class{ } } + async deleteUserPlaylist(socket, data, userDB){ + try{ + //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 the channel doesn't exist + if(userDB.getPlaylistByName(data.playlist) == null){ + //Bitch, moan, complain... + loggerUtils.socketErrorHandler(socket, `Playlist named '${data.playlist}' doesn't exist!`, "validation"); + //and ignore it! + return; + } + + //Delete playlist name + await userDB.deletePlaylistByName(data.playlist); + + //Return playlists from channel doc + this.getUserPlaylists(socket, userDB); + }catch(err){ + return loggerUtils.socketExceptionHandler(socket, err); + } + } + + //Add Media Functions async addToChannelPlaylist(socket, data, chanDB){ try{ //if we wherent handed a channel document @@ -198,6 +314,7 @@ module.exports = class{ } } + //Queuing Functions async queueChannelPlaylist(socket, data, chanDB){ try{ //if we wherent handed a channel document @@ -334,6 +451,7 @@ module.exports = class{ } } + //Rename Channel Playlist async renameChannelPlaylist(socket, data, chanDB){ try{ //if we wherent handed a channel document @@ -387,6 +505,7 @@ module.exports = class{ } } + //Change Default Title Functions async changeDefaultTitlesChannelPlaylist(socket, data, chanDB){ try{ //if we wherent handed a channel document @@ -433,6 +552,7 @@ module.exports = class{ } } + //Delete Playlist Functions async deleteChannelPlaylistMedia(socket, data, chanDB){ try{ //if we wherent handed a channel document diff --git a/src/app/channel/tokebot.js b/src/app/channel/tokebot.js index 421605e..fe0444f 100644 --- a/src/app/channel/tokebot.js +++ b/src/app/channel/tokebot.js @@ -160,10 +160,6 @@ module.exports = class tokebot{ //we need to wait for this so we don't send used tokes pre-maturely await userModel.tattooToke(tokers); - - - - } cooldown(){ diff --git a/src/schemas/channel/channelSchema.js b/src/schemas/channel/channelSchema.js index 8ab4925..42fe614 100644 --- a/src/schemas/channel/channelSchema.js +++ b/src/schemas/channel/channelSchema.js @@ -556,7 +556,6 @@ channelSchema.methods.getPlaylists = function(){ const playlists = []; //For each channel emote - //this.media.playlists.forEach((playlist) => { for(let playlist of this.media.playlists){ //Push an object with select information from the emote to the emote list playlists.push(playlist.dehydrate()); diff --git a/src/schemas/user/userSchema.js b/src/schemas/user/userSchema.js index d1cc9e2..f98a137 100644 --- a/src/schemas/user/userSchema.js +++ b/src/schemas/user/userSchema.js @@ -29,6 +29,7 @@ const flairModel = require('../flairSchema'); const permissionModel = require('../permissionSchema'); const emoteModel = require('../emoteSchema'); const emailChangeModel = require('./emailChangeSchema'); +const playlistSchema = require('../channel/media/playlistSchema'); //Utils const hashUtil = require('../../utils/hashUtils'); const mailUtil = require('../../utils/mailUtils'); @@ -141,7 +142,8 @@ const userSchema = new mongoose.Schema({ alts:[{ type: mongoose.SchemaTypes.ObjectID, ref: "user" - }] + }], + playlists: [playlistSchema] }); //This is one of those places where you really DON'T want to use an arrow function over an anonymous one! @@ -535,6 +537,60 @@ userSchema.methods.deleteEmote = async function(name){ } } +userSchema.methods.getPlaylists = function(){ + //Create an empty array to hold our emote list + const playlists = []; + + //For each channel emote + for(let playlist of this.playlists){ + //Push an object with select information from the emote to the emote list + playlists.push(playlist.dehydrate()); + } + + //return the emote list + return playlists; +} + +userSchema.methods.playlistCrawl = function(cb){ + for(let listIndex in this.playlists){ + //Grab the associated playlist + playlist = this.playlists[listIndex]; + + //Call the callback with the playlist and list index as arguments + cb(playlist, listIndex); + } +} + +userSchema.methods.getPlaylistByName = function(name){ + //Create null value to hold our found playlist + let foundPlaylist = null; + + //Crawl through active playlists + this.playlistCrawl((playlist, listIndex) => { + //If we found a match based on name + if(playlist.name == name){ + //Keep it + foundPlaylist = playlist; + //Pass down the list index + foundPlaylist.listIndex = listIndex; + } + }); + + //return the given playlist + return foundPlaylist; +} + +userSchema.methods.deletePlaylistByName = async function(name){ + //Find the playlist + const playlist = this.getPlaylistByName(name); + + //splice out the given playlist + this.playlists.splice(playlist.listIndex, 1); + + //save the channel document + await this.save(); +} + userSchema.methods.tattooIPRecord = async function(ip){ //Hash the users ip const ipHash = hashUtil.hashIP(ip); diff --git a/src/views/channel.ejs b/src/views/channel.ejs index 8bdea7a..5deb6bb 100644 --- a/src/views/channel.ejs +++ b/src/views/channel.ejs @@ -49,8 +49,8 @@ along with this program. If not, see . %> <%# panels %> - + <%# main client %> diff --git a/www/js/channel/panels/queuePanel/playlistManager.js b/www/js/channel/panels/queuePanel/playlistManager.js index 2bceb96..6d6d51c 100644 --- a/www/js/channel/panels/queuePanel/playlistManager.js +++ b/www/js/channel/panels/queuePanel/playlistManager.js @@ -33,11 +33,14 @@ class playlistManager{ docSwitch(){ //Grab menus this.channelPlaylistDiv = this.panelDocument.querySelector("#queue-channel-playlist-div"); + this.userPlaylistDiv = this.panelDocument.querySelector("#queue-user-playlist-div"); //Grab controls this.createPlaylistSpan = this.panelDocument.querySelector('#queue-add-playlist-span'); this.channelPlaylistLabel = this.panelDocument.querySelector('#queue-channel-playlist-span'); this.channelPlaylistCaret = this.panelDocument.querySelector('#queue-channel-playlist-toggle'); + this.userPlaylistLabel = this.panelDocument.querySelector('#queue-user-playlist-span'); + this.userPlaylistCaret = this.panelDocument.querySelector('#queue-user-playlist-toggle'); //Force playlist re-render to fix controls this.client.socket.emit('getChannelPlaylists'); @@ -49,6 +52,7 @@ class playlistManager{ setupInput(){ this.createPlaylistSpan.addEventListener('click', (event)=>{new newPlaylistPopup(event, this.client, this.queuePanel.ownerDoc)}) this.channelPlaylistLabel.addEventListener('click', this.toggleChannelPlaylists.bind(this)); + this.userPlaylistLabel.addEventListener('click', this.toggleUserPlaylists.bind(this)); } /* queue control button functions */ @@ -71,6 +75,26 @@ class playlistManager{ } } + /* queue control button functions */ + toggleUserPlaylists(event){ + //If the div is hidden + if(this.userPlaylistDiv.style.display == 'none'){ + //Light up the button + this.userPlaylistLabel.classList.add('positive'); + //Flip the caret + this.userPlaylistCaret.classList.replace('bi-caret-right-fill', 'bi-caret-down-fill'); + //Show the div + this.userPlaylistDiv.style.display = ''; + }else{ + //Unlight the button + this.userPlaylistLabel.classList.remove('positive'); + //Flip the caret + this.userPlaylistCaret.classList.replace('bi-caret-down-fill', 'bi-caret-right-fill'); + //Hide the div + this.userPlaylistDiv.style.display = 'none'; + } + } + checkOpenPlaylists(){ //If open map is a string, indicating we just renamed a playlist with it's media open if(typeof this.openMap == 'string'){ diff --git a/www/js/channel/panels/queuePanel.js b/www/js/channel/panels/queuePanel/queuePanel.js similarity index 100% rename from www/js/channel/panels/queuePanel.js rename to www/js/channel/panels/queuePanel/queuePanel.js