diff --git a/src/utils/media/yanker.js b/src/utils/media/yanker.js index 02f9193..1aaef04 100644 --- a/src/utils/media/yanker.js +++ b/src/utils/media/yanker.js @@ -118,7 +118,7 @@ module.exports.getMediaType = async function(url){ } //If we have a match to a dailymotion video - if(match = url.match(/dailymotion\.com\/video\/([a-z0-9]{7})/)){ + if(match = url.match(/dailymotion\.com\/video\/([a-zA-Z0-9]+)/)){ return { type: "dm", id: match[1] diff --git a/src/views/channelSettings.ejs b/src/views/channelSettings.ejs index a32e53d..5a2780d 100644 --- a/src/views/channelSettings.ejs +++ b/src/views/channelSettings.ejs @@ -26,6 +26,7 @@ along with this program. If not, see . %> <%- include('partial/navbar', {user}); %>

<%- channel.name %> - Channel Settings

+ <%- include('partial/channelSettings/description.ejs', {channel}); %> <%- include('partial/channelSettings/userList.ejs'); %> <%- include('partial/channelSettings/banList.ejs'); %> <%- include('partial/channelSettings/settings.ejs'); %> diff --git a/src/views/partial/channelSettings/description.ejs b/src/views/partial/channelSettings/description.ejs new file mode 100644 index 0000000..a2f073f --- /dev/null +++ b/src/views/partial/channelSettings/description.ejs @@ -0,0 +1,31 @@ +<%# Canopy - The next generation of stoner streaming software +Copyright (C) 2024-2025 Rainbownapkin and the TTN Community + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . %> +
+

Channel Info:

+
+ +

Thumbnail:

+
+ + +
+
+ +

Description:

+

<%= channel.description %>

+ +
+
\ No newline at end of file diff --git a/www/css/adminPanel.css b/www/css/adminPanel.css index 25b3676..b570a16 100644 --- a/www/css/adminPanel.css +++ b/www/css/adminPanel.css @@ -58,6 +58,39 @@ img.admin-list-entry-item{ text-align: center; } +#channel-info-div{ + display: flex; + justify-content: space-evenly; + padding: 0.3em 0; +} + +.channel-info-span p{ + margin: 0; +} + +#channel-info-thumbnail-span div{ + position: relative; +} + +#channel-info-description-span{ + flex: 0.5; +} + +#channel-info-thumbnail-prompt{ + position: absolute; + width: 8em; + top: 3em; + left: calc(50% - 4.5em); +} + + +#channel-info-description-prompt{ + resize: vertical; + width: 100%; + min-height: 2em; + max-height: 8em; +} + #channel-rank-title{ margin-bottom: 0; } diff --git a/www/css/theme/movie-night.css b/www/css/theme/movie-night.css index de764eb..d7443ae 100644 --- a/www/css/theme/movie-night.css +++ b/www/css/theme/movie-night.css @@ -302,6 +302,11 @@ p.channel-guide-entry-item{ background-color: var(--bg1-alt0); } +/* channel settings */ +.channel-info-label{ + color: var(--accent1-alt0); +} + /* channel */ #media-panel-div{ background-color: black; diff --git a/www/js/channel/player.js b/www/js/channel/player.js index dac0a13..2b7ccd1 100644 --- a/www/js/channel/player.js +++ b/www/js/channel/player.js @@ -99,6 +99,8 @@ class player{ }else if(data.media.type == "livehls"){ //Create a new HLS Livestream Handler for it this.mediaHandler = new hlsLiveStreamHandler(this.client, this, data.media); + }else if(data.media.type == 'dm'){ + this.mediaHandler = new hlsDailymotionHandler(this.client, this, data.media); //Otherwise, if we have a raw-file compatible source }else if(data.media.type == 'ia' || data.media.type == 'raw' || data.media.type == 'yt' || data.media.type == 'dm'){ //Create a new raw file handler for it diff --git a/www/js/channelSettings.js b/www/js/channelSettings.js index 980b1cb..1713335 100644 --- a/www/js/channelSettings.js +++ b/www/js/channelSettings.js @@ -19,6 +19,7 @@ class channelSettingsPage{ //Get channel name off of the URL this.channel = window.location.pathname.slice(3).replace('/settings',''); //Instantiate UX handling objects, making sure to pass the channel name. + this.chanInfo = new chanInfo(this.channel); this.rankList = new rankList(this.channel); this.banList = new banList(this.channel); this.permList = new permList(this.channel); @@ -29,6 +30,81 @@ class channelSettingsPage{ } } +class chanInfo{ + constructor(channel){ + //Define channel + this.channel = channel; + //Define UX elements + this.thumbnail = document.querySelector("#channel-info-thumbnail"); + this.thumbnailInput = document.querySelector("#channel-info-thumbnail-prompt"); + this.description = document.querySelector("#channel-info-description"); + //Create description prompt + this.descriptionInput = document.createElement("textarea"); + this.descriptionInput.id = "channel-info-description-prompt"; + + //Setup Input Event Handlers + this.setupInput(); + } + + setupInput(){ + this.thumbnail.addEventListener('click', ()=>{this.toggleThumbnailPrompt(true)}); + this.thumbnailInput.addEventListener('keydown', this.submitThumbnail.bind(this)); + + this.description.addEventListener('click', ()=>{this.toggleDescriptionPrompt(true)}) + this.descriptionInput.addEventListener('keydown', this.submitDescription.bind(this)); + } + + toggleThumbnailPrompt(enabled){ + this.thumbnailInput.style.display = enabled ? "" : "none"; + + if(enabled){ + this.thumbnail.classList.remove('interactive'); + }else{ + this.thumbnail.classList.add('interactive'); + } + } + + submitThumbnail(event){ + //If we hit didnt hit enter or escape + if(!(event.key == "Enter" || event.key == "Escape") && event.key != null){ + //bail! + return; + } + + //Toggle prompt + this.toggleThumbnailPrompt(false); + + //Only returns after this point + if(event.key != "Enter" && event.key != null){ + return; + } + } + + toggleDescriptionPrompt(enabled){ + if(enabled){ + this.description.replaceWith(this.descriptionInput); + }else{ + this.descriptionInput.replaceWith(this.description); + } + } + + submitDescription(event){ + //If we hit didnt hit enter (without shift) or escape + if(!((event.key == "Enter" && !event.shiftKey) || event.key == "Escape") && event.key != null){ + //bail! + return; + } + + //Toggle prompt + this.toggleDescriptionPrompt(false); + + //Only returns after this point + if(event.key != "Enter" && !event.shiftKey && event.key != null){ + return; + } + } +} + class rankList{ constructor(channel){ this.channel = channel @@ -52,7 +128,7 @@ class rankList{ } async submitNewRank(event){ - if(event.key != "Enter" && event.key != null){ + if((event.key != "Enter" ) && event.key != null){ //Bail out if we didn't hit enter return; }