diff --git a/src/app/channel/connectedUser.js b/src/app/channel/connectedUser.js index bec1409..5347cb4 100644 --- a/src/app/channel/connectedUser.js +++ b/src/app/channel/connectedUser.js @@ -197,8 +197,6 @@ class connectedUser{ chanDB = await channelModel.findOne({name: this.channel.name}); } - //If our perm map is un-initiated - //can't set this in constructor easily since it's asyncornous //need to wait for it to complete before sending this off, but shouldnt re-do the wait for later connections if(this.permMap == null){ //Grab perm map @@ -238,7 +236,7 @@ class connectedUser{ const chatBuffer = this.channel.chatBuffer.buffer; //Send off the metadata to our user's clients - this.emit("clientMetadata", {user: userObj, flairList, queueLock, chatBuffer}); + this.emit("clientMetadata", {user: userObj, flairList, queueLock, chatBuffer, streamType: chanDB.settings.streamType}); } /** diff --git a/src/app/channel/media/queue.js b/src/app/channel/media/queue.js index 597c062..ac2eb75 100644 --- a/src/app/channel/media/queue.js +++ b/src/app/channel/media/queue.js @@ -120,6 +120,7 @@ class queue{ socket.on("move", (data) => {this.moveMedia(socket, data)}); socket.on("lock", () => {this.toggleLock(socket)}); socket.on("goLive", (data) => {this.goLive(socket, data)}); + socket.on("getStreamType", (data) => {this.getStreamType(socket)}); //If debug mode is enabled if(config.debug){ @@ -357,6 +358,18 @@ class queue{ throw loggerUtils.exceptionSmith(`Unable to find channel document ${this.channel.name} while queue item!`, "queue"); } + + //If the user doesn't have permission to be doing this shit + if(!((!this.locked && await chanDB.permCheck(socket.user, 'scheduleMedia')) || await chanDB.permCheck(socket.user, 'scheduleAdmin'))){ + //Fuggettabouttit! + return; + } + + if(chanDB.settings.streamType == "webrtc"){ + console.log("WEBRTC ENABLED, BAILING!"); + return; + } + //If something is playing if(this.nowPlaying != null){ //Capture currently playing object @@ -403,7 +416,7 @@ class queue{ ); //Validate mode input, and default to overwrite - this.liveMode = (data.mode == "pushback") ? "pushback" : "overwrite"; + this.liveMode = (data != null && data.mode == "pushback") ? "pushback" : "overwrite"; //Throw stream lock this.streamLock = true; @@ -415,6 +428,22 @@ class queue{ } } + /** + * Returns current channel stream type upon request + * @param {Socket} socket - Socket we received the request from + */ + async getStreamType(socket){ + try{ + //Get the current channel from the database + const chanDB = await channelModel.findOne({name: socket.chan}); + + //Send streamtype back down to user (Not perm checking for something someone can figure out by looking at a running livestream) + socket.emit("streamType", {streamType: chanDB.settings.streamType}); + }catch(err){ + return loggerUtils.socketExceptionHandler(socket, err); + } + } + async dumpQueue(socket, data){ //If we somehow got here while config.debug is disabled, or the user isn't allowed to preform server-side debugging if(!(config.debug && await permissionModel.permCheck(socket.user, "debug"))){ diff --git a/src/schemas/channel/channelSchema.js b/src/schemas/channel/channelSchema.js index b6df0b1..8bfa484 100644 --- a/src/schemas/channel/channelSchema.js +++ b/src/schemas/channel/channelSchema.js @@ -35,6 +35,11 @@ const chatSchema = require('./chatSchema'); //Utils const { exceptionHandler, errorHandler } = require('../../utils/loggerUtils'); +/** + * "Enum" for emote type property + */ +const streamMethodEnum = ["webrtc","hls"]; + /** * DB Schema for Documents containing de-hydrated representations of Canopy Stream/Chat Channels */ @@ -71,6 +76,12 @@ const channelSchema = new mongoose.Schema({ streamURL: { type: mongoose.SchemaTypes.String, default: '' + }, + streamType: { + type: mongoose.SchemaTypes.String, + enum: streamMethodEnum, + default: streamMethodEnum[0] + } }, permissions: { diff --git a/src/validators/channelValidator.js b/src/validators/channelValidator.js index 92ef4b5..9c87f55 100644 --- a/src/validators/channelValidator.js +++ b/src/validators/channelValidator.js @@ -20,15 +20,6 @@ const { checkSchema, checkExact } = require('express-validator'); //local imports const accountValidator = require('./accountValidator'); -module.exports = { - - settingsMap: () => checkExact(checkSchema({ - 'settingsMap.hidden': { - optional: true, - isBoolean: true, - } })) -} - module.exports.name = function(field = 'name'){ return checkSchema({ [field]: { @@ -89,6 +80,13 @@ module.exports.settingsMap = function(){ } }, errorMessage: "Invalid Stream URL" + }, + 'settingsMap.streamType': { + optional: true, + errorMessage: "Invalid Stream Type", + matches: { + options: [/\b(?:webrtc|hls)\b/] + } } }) ); diff --git a/src/views/partial/channelSettings/settings.ejs b/src/views/partial/channelSettings/settings.ejs index ec740eb..d8dd2ea 100644 --- a/src/views/partial/channelSettings/settings.ejs +++ b/src/views/partial/channelSettings/settings.ejs @@ -17,7 +17,11 @@ along with this program. If not, see . %>

Channel Preferences:

- <% Object.keys(channel.settings).forEach((key) => { %> + <% for(key of Object.keys(channel.settings)){ + //Ignore keys w/ custom HTML + if(key == "streamURL" || key == "streamType"){ + break; + }%> <% switch(typeof channel.settings[key]){ @@ -29,7 +33,22 @@ along with this program. If not, see . %> <% break; } %> - <% }); %> + <% }; %> + +

Livestream Type:

+ + + checked <% }%> > + + + + checked <% }%> > + + + disabled <%}%> id="channel-preference-list-stream-url" placeholder="Stream URL" value="<%= channel.settings["streamURL"] %>"> + + +
\ No newline at end of file diff --git a/www/css/adminPanel.css b/www/css/adminPanel.css index dbcf120..1141bf5 100644 --- a/www/css/adminPanel.css +++ b/www/css/adminPanel.css @@ -139,6 +139,16 @@ img.admin-list-entry-item{ width: 8em; } +#channel-preference-list-stream-settings-grid{ + display: grid; + grid-template-columns: auto auto; + row-gap: 0.5em; +} + +#channel-preference-list-stream-url { + grid-column: 2; +} + div.toke-command-list{ display: grid; grid-template-columns: repeat(auto-fit, minmax(15em, 1fr)); diff --git a/www/js/channel/channel.js b/www/js/channel/channel.js index 5663d48..840b939 100644 --- a/www/js/channel/channel.js +++ b/www/js/channel/channel.js @@ -117,6 +117,10 @@ class channel{ this.queueLock = data.locked; }); + this.socket.on("streamType", (data) => { + this.streamType = data.streamType; + }); + this.queueBroadcastSocket.on("queue", (data) => { this.queue = new Map(data.queue); }); @@ -146,6 +150,9 @@ class channel{ //Store queue lock status this.queueLock = data.queueLock; + + //Store streamType + this.streamType = data.streamType; } /** diff --git a/www/js/channel/panels/queuePanel/queuePanel.js b/www/js/channel/panels/queuePanel/queuePanel.js index c254caa..7f258e3 100644 --- a/www/js/channel/panels/queuePanel/queuePanel.js +++ b/www/js/channel/panels/queuePanel/queuePanel.js @@ -54,6 +54,8 @@ class queuePanel extends panelObj{ */ this.playlistManager = new playlistManager(client, panelDocument, this); + this.streamType = ""; + //Define non-input event listeners this.defineListeners(); } @@ -65,6 +67,9 @@ class queuePanel extends panelObj{ //Call derived doc switch function super.docSwitch(); + //Request Channel Stream Type + this.client.socket.emit('getStreamType'); + //Get queue div this.queue = this.panelDocument.querySelector('#queue'); //Get queue container @@ -243,6 +248,10 @@ class queuePanel extends panelObj{ } } + handleStreamType(data){ + + } + /* queue control button functions */ /** @@ -269,6 +278,9 @@ class queuePanel extends panelObj{ * @param {Event} event - Event passed down from Event Listener */ toggleGoLive(event){ + //Request stream type from the server Just-InCaseā„¢ + client.socket.emit('getStreamType'); + //If we're not livestreaming if(client.player.mediaHandler.type != "livehls"){ //If the div is hidden diff --git a/www/js/channelSettings.js b/www/js/channelSettings.js index 36fa99a..74bbe49 100644 --- a/www/js/channelSettings.js +++ b/www/js/channelSettings.js @@ -24,6 +24,7 @@ class channelSettingsPage{ this.banList = new banList(this.channel); this.permList = new permList(this.channel); this.prefrenceList = new prefrenceList(this.channel); + this.livestreamPreferences = new livestreamPreferences(this.channel); this.tokeCommandList = new tokeCommandList(this.channel); this.emoteList = new emoteList(this.channel); this.deleteBtn = new deleteBtn(this.channel); @@ -364,6 +365,72 @@ class prefrenceList{ } } +class livestreamPreferences{ + constructor(channel){ + this.channel = channel; + + //Grab inputs + this.webRTCButton = document.querySelector("#channel-preference-list-stream-type-webrtc"); + this.hlsButton = document.querySelector("#channel-preference-list-stream-type-hls"); + this.streamURL = document.querySelector("#channel-preference-list-stream-url"); + + //Setup Input + this.setupInput(); + } + + setupInput(){ + //Add event listeners + this.webRTCButton.addEventListener("change", this.submitTypeUpdate.bind(this)); + this.hlsButton.addEventListener("change", this.submitTypeUpdate.bind(this)); + this.streamURL.addEventListener("change",this.submitURLUpdate.bind(this)); + } + + async submitURLUpdate(event){ + //Grab value from event target + const value = event.target.value; + + //Send setting change request to server, and store update once its received + const update = await utils.ajax.setChannelSetting(this.channel, new Map([["streamURL", value]])); + + //If we we're given a good update + if(update != null && update.streamURL != null){ + //Display the new streamURL in the input (I'm not making a dedicated method for a fuckin' one-liner) + this.streamURL.value = update.streamURL + } + } + + async submitTypeUpdate(event){ + //Grab value from event target + const value = event.target.value; + + //Send setting change request to server, and store update once its received + const update = await utils.ajax.setChannelSetting(this.channel, new Map([["streamType", value]])); + + //Pass update to the handleUpdate method + this.handleTypeUpdate(update); + } + + handleTypeUpdate(data){ + //If the stream type is HLS + if(data.streamType == "hls"){ + //Ensure the hls button is checked + this.hlsButton.checked = true; + //Ensure the stream url input is enabled + this.streamURL.disabled = false; + //Otherwise + }else{ + //Ensure the stream url input is disabled + this.streamURL.disabled = true; + } + + //If the stream type is webrtc + if(data.streamType == "webrtc"){ + //Ensure the webrtc button is checked + this.webRTCButton.checked = true; + } + } +} + class permList{ constructor(channel){ this.channel = channel