Beta ready

This commit is contained in:
nuttylmao
2025-09-15 08:07:00 +10:00
parent 6f69be969c
commit 9033e5788b
6 changed files with 375 additions and 86 deletions
+3 -1
View File
@@ -7,12 +7,14 @@
--background-color: #181818;
--accent-color: #2196f3;
--success-color: #3be477;
--confirm-flash: #3be47680;
--button-color: #2e2e2e;
--dialog-background: #1d1d1d;
--callout-background: #181818;
--error-background: #a82d2e33;
--error-color: #c65e5e;
--mandatory-field-color: #c93f3f;
--error-text-color: #c93f3f;
}
@@ -99,7 +101,7 @@ button:hover {
button:disabled {
opacity: 0.3;
cursor: inherit;
cursor: not-allowed;
}
.icon-button {
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

+93 -36
View File
@@ -8,7 +8,7 @@
<body>
<!-- Update All Button -->
<!-- Update All Button -->
<button onclick="OpenUpdateAllDialog()">
<img src="https://api.iconify.design/material-symbols:edit-square-outline.svg?color=%23ffffff"> Update All
</button>
@@ -20,7 +20,7 @@
<!-- YouTube warning -->
<div id="youtube-warning" class="broadcast">
<img class="platform-icon" id="platform-icon" src="icons/platforms/youtube.png"/>
<img class="platform-icon" id="platform-icon" src="icons/platforms/youtube.png" />
<div class="broadcast-info">
<label class="setting-attribute">
@@ -29,121 +29,175 @@
</div>
<div id="broadcast-buttons">
<button id="broadcast-dashboard-button" class="icon-button" title="Dashboard"><img class="button-icon" src="https://api.iconify.design/gridicons:popout.svg?color=%23ffffff"></button>
<button id="broadcast-dashboard-button" class="icon-button" title="Dashboard"><img class="button-icon"
src="https://api.iconify.design/gridicons:popout.svg?color=%23ffffff"></button>
</div>
</div>
</div>
<!-- Blur Layer -->
<div id="blur-layer" class="blur"></div>
<!-- Blur Layer -->
<div id="blur-layer" class="blur"></div>
<!-- Update All Popup -->
<div id="update-all-dialog" class="dialog">
<button class="dialog-nav-button" onclick="CloseUpdateAllDialog()">×</button>
<label class="title" style="text-align: center;">Stream Info</label>
<div class="field">
<label>Title</label>
<input type="text" id="update-all-title-input" placeholder="My First Stream #RoadToAffiliate">
<div style="display: flex; flex-direction: row; align-items: end; gap: 1em;">
<label>Title</label>
<div id="all-title-char-limit" class="char-limit">
<span id="all-title-char-count"></span>/<span id="all-title-char-max"></span>
</div>
</div>
<input type="text" id="all-title-input" placeholder="Leave empty to keep current title">
</div>
<div class="field">
<label>Category</label>
<input type="text" id="update-all-category-input" placeholder="Just Chatting">
<input type="text" id="all-category-input" placeholder="Leave empty to keep current category">
</div>
<button onclick="UpdateAllSubmit()">Update All</button>
<button id="all-submit-button" onclick="UpdateAllSubmit()">Update All</button>
</div>
<!-- Update Twitch Popup -->
<div id="update-twitch-dialog" class="dialog">
<button class="dialog-nav-button" onclick="CloseUpdateTwitchDialog()">×</button>
<div style="display: flex; flex-direction: row; align-items: center; justify-content: center; gap: 1em;">
<img src="icons/platforms/twitch.png" class="platform-icon">
<label class="title" style="text-align: center;">Stream Info</label>
</div>
<div class="field">
<label>Title</label>
<input type="text" id="update-twitch-title-input" placeholder="My First Stream #RoadToAffiliate">
<div style="display: flex; flex-direction: row; align-items: end; gap: 1em;">
<label>Title</label>
<div id="twitch-title-char-limit" class="char-limit">
<span id="twitch-title-char-count"></span>/<span id="twitch-title-char-max"></span>
</div>
</div>
<input type="text" id="twitch-title-input" placeholder="Leave empty to keep current title">
</div>
<div class="field">
<label>Category</label>
<input type="text" id="update-twitch-category-input" placeholder="Just Chatting">
<input type="text" id="twitch-category-input" placeholder="Leave empty to keep current category">
</div>
<div class="field">
<label>Tags</label>
<input type="text" id="update-twitch-tags-input" placeholder="Retro, Speedrun, CreamTeam">
<div style="display: flex; flex-direction: row; align-items: end; gap: 1em;">
<label>Tags</label>
<div id="twitch-tags-char-limit" class="char-limit">
<span id="twitch-tags-too-long-warning">Tag too long • </span><span id="twitch-tags-char-count"></span>/<span id="twitch-tags-char-max"></span> Tags
</div>
</div>
<label class="setting-description">Add up to 10 tags. Each tag can be 25 characters long with no spaces or special characters.</label>
<input type="text" id="twitch-tags-input" placeholder="Leave empty to keep current tags">
</div>
<button onclick="UpdateTwitchSubmit()">Update</button>
<button id="twitch-submit-button" onclick="UpdateTwitchSubmit()">Update</button>
</div>
<!-- Update Kick Popup -->
<div id="update-kick-dialog" class="dialog">
<button class="dialog-nav-button" onclick="CloseUpdateKickDialog()">×</button>
<div style="display: flex; flex-direction: row; align-items: center; justify-content: center; gap: 1em;">
<img src="icons/platforms/kick.png" class="platform-icon">
<label class="title" style="text-align: center;">Stream Info</label>
</div>
<div class="field">
<label>Title</label>
<input type="text" id="update-kick-title-input" placeholder="My First Stream #RoadToAffiliate">
<div style="display: flex; flex-direction: row; align-items: end; gap: 1em;">
<label>Title</label>
<div id="kick-title-char-limit" class="char-limit">
<span id="kick-title-char-count"></span>/<span id="kick-title-char-max"></span>
</div>
</div>
<input type="text" id="kick-title-input" placeholder="Leave empty to keep current title">
</div>
<div class="field">
<label>Category</label>
<input type="text" id="update-kick-category-input" placeholder="Just Chatting">
<input type="text" id="kick-category-input" placeholder="Leave empty to keep current category">
</div>
<button onclick="UpdateKickSubmit()">Update</button>
<button id="kick-submit-button" onclick="UpdateKickSubmit()">Update</button>
</div>
<!-- Update YouTube Popup -->
<div id="update-youtube-dialog" class="dialog">
<button class="dialog-nav-button" onclick="CloseUpdateYouTubeDialog()">×</button>
<div style="display: flex; flex-direction: row; align-items: center; justify-content: center; gap: 1em;">
<img src="icons/platforms/youtube.png" class="platform-icon">
<label class="title" style="text-align: center;">Stream Info</label>
</div>
<div class="field">
<label>Title</label>
<input type="text" id="update-youtube-title-input" placeholder="My First Stream #RoadToAffiliate">
<div style="display: flex; flex-direction: row; align-items: end; gap: 1em;">
<label>Title</label>
<div id="youtube-title-char-limit" class="char-limit">
<span id="youtube-title-char-count"></span>/<span id="youtube-title-char-max"></span>
</div>
</div>
<input type="text" id="youtube-title-input" placeholder="Leave empty to keep current title">
</div>
<div class="field">
<label>Description</label>
<textarea type="text" id="update-youtube-description-input" placeholder="I'm describing so hard right now..." class="textarea-description"></textarea>
<div style="display: flex; flex-direction: row; align-items: end; gap: 1em;">
<label>Description</label>
<div id="youtube-description-char-limit" class="char-limit">
<span id="youtube-description-char-count"></span>/<span id="youtube-description-char-max"></span>
</div>
</div>
<textarea type="text" id="youtube-description-input" placeholder="Leave empty to keep current description"
class="textarea-description"></textarea>
</div>
<div class="field">
<label>Category</label>
<input type="text" id="update-youtube-category-input" placeholder="Gaming">
<select id="youtube-category-input">
<option value="Autos & Vehicles">Autos & Vehicles</option>
<option value="Comedy">Comedy</option>
<option value="Education">Education</option>
<option value="Entertainment">Entertainment</option>
<option value="Film & Animation">Film & Animation</option>
<option value="Gaming">Gaming</option>
<option value="Howto & Style">Howto & Style</option>
<option value="Music">Music</option>
<option value="News & Politics">News & Politics</option>
<option value="Nonprofits & Activism">Nonprofits & Activism</option>
<option value="People & Blogs">People & Blogs</option>
<option value="Pets & Animals">Pets & Animals</option>
<option value="Science & Technology">Science & Technology</option>
<option value="Sports">Sports</option>
<option value="Travel & Events">Travel & Events</option>
</select>
</div>
<div class="field">
<label>Tags</label>
<input type="text" id="update-youtube-tags-input" placeholder="Retro, Speedrun, CreamTeam">
<div style="display: flex; flex-direction: row; align-items: end; gap: 1em;">
<label>Tags</label>
<div id="youtube-tags-char-limit" class="char-limit">
<span id="youtube-tags-char-count"></span>/<span id="youtube-tags-char-max"></span>
</div>
</div>
<input type="text" id="youtube-tags-input" placeholder="Leave empty to keep current tags">
</div>
<div class="field">
<label>Visibility</label>
<select id="update-youtube-privacy-select">
<select id="youtube-privacy-select">
<option value="private">Private</option>
<option value="unlisted">Unlisted</option>
<option value="public">Public</option>
</select>
</div>
<button onclick="UpdateYouTubeSubmit()">Update</button>
<button id="youtube-submit-button" onclick="UpdateYouTubeSubmit()">Update</button>
</div>
</body>
@@ -152,17 +206,20 @@
<!-- Broadcast Template -->
<template id="broadcast-template">
<div class="broadcast">
<img class="platform-icon" id="platform-icon" src=""/>
<img class="platform-icon" id="platform-icon" src="" />
<div class="broadcast-info">
<label id="broadcast-title"></label>
<label id="broadcast-category" class="setting-description"></label>
<label id="kick-warning" class="setting-attribute"><br><i>Stream info only available when live — Showing last known stream title</i></label>
<label id="kick-warning" class="setting-attribute"><br><i>Stream info only available when live — Showing
last known stream title</i></label>
</div>
<div id="broadcast-buttons">
<button id="broadcast-stream-button" class="icon-button" title="Open Stream"><img class="button-icon" src="https://api.iconify.design/streamline:button-play-solid.svg?color=%23ffffff"></button>
<button id="broadcast-dashboard-button" class="icon-button" title="Dashboard"><img class="button-icon" src="https://api.iconify.design/gridicons:popout.svg?color=%23ffffff"></button>
<button id="broadcast-stream-button" class="icon-button" title="Open Stream"><img class="button-icon"
src="https://api.iconify.design/streamline:button-play-solid.svg?color=%23ffffff"></button>
<button id="broadcast-dashboard-button" class="icon-button" title="Dashboard"><img class="button-icon"
src="https://api.iconify.design/gridicons:popout.svg?color=%23ffffff"></button>
<button id="broadcast-edit-button" class="icon-button flip-that-shit-homie" title="Edit"></button>
</div>
</div>
+269 -48
View File
@@ -106,10 +106,17 @@ async function AddBroadcast(data) {
const existingDiv = broadcastList.querySelector(`#${data.id}`);
// Create a new instance of the template
const instance = existingDiv ? existingDiv : template.content.cloneNode(true);
let instance;
if (existingDiv)
instance = existingDiv;
else {
instance = template.content.firstElementChild.cloneNode(true);
instance.id = data.id;
broadcastList.appendChild(instance);
}
// Get divs
const broadcastEl = instance.querySelector('.broadcast');
const platformIconEl = instance.querySelector('#platform-icon');
const titleEl = instance.querySelector('#broadcast-title');
const categoryEl = instance.querySelector('#broadcast-category');
@@ -118,21 +125,6 @@ async function AddBroadcast(data) {
const dashboardButtonEl = instance.querySelector('#broadcast-dashboard-button');
const editButtonEl = instance.querySelector('#broadcast-edit-button');
// Set properties
if (!existingDiv)
broadcastEl.id = data.id;
// Set the platform icon
platformIconEl.src = `icons/platforms/${data.platform}.png`;
// Set the stream title
if (data.title)
titleEl.textContent = data.title;
// Set the stream category
if (data.category)
categoryEl.textContent = data.category;
// Streamer.bot does not provide title/category for Kick, so pull from API
if (data.platform == 'kick')
{
@@ -142,14 +134,14 @@ async function AddBroadcast(data) {
// The current title is only provided if the stream if currently live
if (response_data.livestream)
{
titleEl.textContent = response_data.livestream.session_title;
categoryEl.textContent = response_data.livestream.categories[0].name;
data.title = response_data.livestream.session_title;
data.category = response_data.livestream.categories[0].name;
kickWarningEl.style.display = 'none';
}
else if (response_data.previous_livestreams)
else if (response_data.previous_livestreams.length > 0)
{
titleEl.textContent = response_data.previous_livestreams[0].session_title;
categoryEl.textContent = response_data.previous_livestreams[0].categories[0].name;
data.title = response_data.previous_livestreams[0].session_title;
data.category = response_data.previous_livestreams[0].categories[0].name;
kickWarningEl.style.display = 'inline';
}
else
@@ -157,6 +149,37 @@ async function AddBroadcast(data) {
kickWarningEl.style.display = 'inline';
}
}
// Flash green to show that it updated
if (titleEl.textContent != data.title || categoryEl.textContent != data.category)
{
instance.style.backgroundColor = getComputedStyle(document.documentElement).getPropertyValue('--confirm-flash');
setTimeout(() => {
instance.style.backgroundColor = '';
}, 1000);
}
// Set the platform icon
platformIconEl.src = `icons/platforms/${data.platform}.png`;
// Special logo for YouTube shorts
if (data.platform == 'youtube') {
const targets = ["vertical", "shorts"];
const isShort = data.tags.some(item =>
targets.some(target => item.toLowerCase() === target.toLowerCase())
);
if (isShort)
platformIconEl.src = `icons/platforms/youtube-shorts.png`;
}
// Set the stream title
if (data.title)
titleEl.textContent = data.title;
// Set the stream category
if (data.category)
categoryEl.textContent = data.category;
streamButtonEl.onclick = function() {
//window.open(data.streamUrl, '_blank');
@@ -171,29 +194,31 @@ async function AddBroadcast(data) {
editButtonEl.onclick = function() {
switch (data.platform) {
case 'twitch':
document.getElementById('update-twitch-title-input').value = titleEl.textContent;
document.getElementById('update-twitch-category-input').value = categoryEl.textContent;
document.getElementById('twitch-title-input').value = data.title;
document.getElementById('twitch-category-input').value = data.category;
document.getElementById('twitch-tags-input').value = data.tags.join(", ");
ValidateTwitchDialog();
updateTwitchDialog.style.display = "flex";
break;
case 'kick':
document.getElementById('update-kick-title-input').value = titleEl.textContent;
document.getElementById('update-kick-category-input').value = categoryEl.textContent;
document.getElementById('kick-title-input').value = titleEl.textContent;
document.getElementById('kick-category-input').value = categoryEl.textContent;
ValidateKickDialog();
updateKickDialog.style.display = "flex";
break;
case 'youtube':
currentBroadcastId = broadcastEl.id;
document.getElementById('update-youtube-title-input').value = data.title;
document.getElementById('update-youtube-description-input').value = data.description;
document.getElementById('update-youtube-category-input').value = data.category;
document.getElementById('update-youtube-privacy-select').value = data.privacy;
currentBroadcastId = data.id;
document.getElementById('youtube-title-input').value = data.title;
document.getElementById('youtube-description-input').value = data.description;
document.getElementById('youtube-category-input').value = data.category;
document.getElementById('youtube-tags-input').value = data.tags.join(", ");
document.getElementById('youtube-privacy-select').value = data.privacy;
ValidateYouTubeDialog();
updateYouTubeDialog.style.display = "flex";
break;
}
blurLayer.style.display = "block";
};
if (!existingDiv)
broadcastList.appendChild(instance);
}
@@ -216,8 +241,6 @@ async function OpenURL(url) {
///////////////////////
// PAGE INTERACTIONS //
///////////////////////
@@ -254,8 +277,8 @@ async function UpdateAllSubmit() {
},
args = {
platform: 'all',
title: document.getElementById('update-all-title-input').value,
category: document.getElementById('update-all-category-input').value
title: document.getElementById('all-title-input').value,
category: document.getElementById('all-category-input').value
}
);
@@ -269,9 +292,9 @@ async function UpdateTwitchSubmit() {
},
args = {
platform: 'twitch',
title: document.getElementById('update-twitch-title-input').value,
category: document.getElementById('update-twitch-category-input').value,
tags: document.getElementById('update-twitch-tags-input').value
title: document.getElementById('twitch-title-input').value,
category: document.getElementById('twitch-category-input').value,
tags: document.getElementById('twitch-tags-input').value
}
);
@@ -285,8 +308,8 @@ async function UpdateKickSubmit() {
},
args = {
platform: 'kick',
title: document.getElementById('update-kick-title-input').value,
category: document.getElementById('update-kick-category-input').value
title: document.getElementById('kick-title-input').value,
category: document.getElementById('kick-category-input').value
}
);
@@ -300,11 +323,11 @@ async function UpdateYouTubeSubmit() {
},
args = {
platform: 'youtube',
title: document.getElementById('update-youtube-title-input').value,
description: document.getElementById('update-youtube-description-input').value,
category: document.getElementById('update-youtube-category-input').value,
tags: document.getElementById('update-youtube-tags-input').value,
privacy: document.getElementById('update-youtube-privacy-select').value,
title: document.getElementById('youtube-title-input').value,
description: document.getElementById('youtube-description-input').value,
category: document.getElementById('youtube-category-input').value,
tags: document.getElementById('youtube-tags-input').value,
privacy: document.getElementById('youtube-privacy-select').value,
broadcastId: currentBroadcastId
}
);
@@ -314,6 +337,204 @@ async function UpdateYouTubeSubmit() {
/////////////////////
// DATA VALIDATION //
/////////////////////
const GLOBAL_TITLE_MAX = 100;
const TWITCH_TITLE_MAX = 140;
const TWITCH_TAGS_MAX = 10;
const KICK_TITLE_MAX = 100;
const YOUTUBE_TITLE_MAX = 100;
const YOUTUBE_DESCRIPTION_MAX = 5000;
const YOUTUBE_TAGS_MAX = 500;
const allTitleInput = document.getElementById("all-title-input");
const twitchTitleInput = document.getElementById("twitch-title-input");
const twitchTagsInput = document.getElementById("twitch-tags-input");
const kickTitleInput = document.getElementById("kick-title-input");
const youtubeTitleInput = document.getElementById("youtube-title-input");
const youtubeDescriptionInput = document.getElementById("youtube-description-input");
const youtubeTagsInput = document.getElementById("youtube-tags-input");
// Function to update char count and handle validation
function ValidateUpdateAllDialog() {
// Get references to elements
const allTitleCharLimit = document.getElementById('all-title-char-limit');
const allCharCount = document.getElementById('all-title-char-count');
const allTitleCharMax = document.getElementById('all-title-char-max');
const allUpdateButton = document.getElementById('all-submit-button');
// Validate title field
const currentLength = allTitleInput.value.length;
allCharCount.textContent = currentLength;
allTitleCharMax.textContent = GLOBAL_TITLE_MAX;
if (currentLength > GLOBAL_TITLE_MAX)
allTitleCharLimit.style.color = getComputedStyle(document.documentElement).getPropertyValue('--error-text-color');
else
allTitleCharLimit.style.color = ""; // reset to default
// Set button interactability (yes, that is a word — look it up)
if (currentLength > GLOBAL_TITLE_MAX)
allUpdateButton.disabled = true;
else
allUpdateButton.disabled = false;
}
// Function to update char count and handle validation
function ValidateTwitchDialog() {
// Get references to elements
const twitchTitleCharLimit = document.getElementById('twitch-title-char-limit');
const twitchCharCount = document.getElementById('twitch-title-char-count');
const twitchTitleCharMax = document.getElementById('twitch-title-char-max');
const twitchTagsCharLimit = document.getElementById('twitch-tags-char-limit');
const twitchTagsCharCount = document.getElementById('twitch-tags-char-count');
const twitchTagsCharMax = document.getElementById('twitch-tags-char-max');
const twitchTagsTooLongWarning = document.getElementById('twitch-tags-too-long-warning');
const twitchUpdateButton = document.getElementById('twitch-submit-button');
// Validate title field
const currentLength = twitchTitleInput.value.length;
twitchCharCount.textContent = currentLength;
twitchTitleCharMax.textContent = TWITCH_TITLE_MAX;
if (currentLength > TWITCH_TITLE_MAX)
twitchTitleCharLimit.style.color = getComputedStyle(document.documentElement).getPropertyValue('--error-text-color');
else
twitchTitleCharLimit.style.color = ""; // reset to default
// Validate tags field
// Split by comma, trim each tag, sum lengths
const tagsArray = twitchTagsInput.value
.split(',')
.map(tag => tag.trim())
.filter(tag => tag.length > 0);
const tagsLength = tagsArray.length;
twitchTagsCharCount.textContent = tagsLength;
twitchTagsCharMax.textContent = TWITCH_TAGS_MAX;
if (tagsLength > TWITCH_TAGS_MAX)
twitchTagsCharLimit.style.color = getComputedStyle(document.documentElement).getPropertyValue('--error-text-color');
else
twitchTagsCharLimit.style.color = ""; // reset to default
// Also check that each tag is under 25 characters
const hasLongTag = tagsArray.some(tag => tag.length > 25);
if (hasLongTag) {
twitchTagsTooLongWarning.style.display = 'inline';
twitchTagsCharLimit.style.color = getComputedStyle(document.documentElement).getPropertyValue('--error-text-color');
}
else {
twitchTagsTooLongWarning.style.display = 'none';
twitchTagsCharLimit.style.color = ""; // reset to default
}
// Set button interactability (yes, that is a word — look it up)
if (currentLength > TWITCH_TITLE_MAX || tagsLength > TWITCH_TAGS_MAX || hasLongTag)
twitchUpdateButton.disabled = true;
else
twitchUpdateButton.disabled = false;
}
// Function to update char count and handle validation
function ValidateKickDialog() {
// Get references to elements
const kickTitleCharLimit = document.getElementById('kick-title-char-limit');
const kickCharCount = document.getElementById('kick-title-char-count');
const kickTitleCharMax = document.getElementById('kick-title-char-max');
const kickUpdateButton = document.getElementById('kick-submit-button');
// Validate title field
const currentLength = kickTitleInput.value.length;
kickCharCount.textContent = currentLength;
kickTitleCharMax.textContent = KICK_TITLE_MAX;
if (currentLength > KICK_TITLE_MAX)
kickTitleCharLimit.style.color = getComputedStyle(document.documentElement).getPropertyValue('--error-text-color');
else
kickTitleCharLimit.style.color = ""; // reset to default
// Set button interactability (yes, that is a word — look it up)
if (currentLength > KICK_TITLE_MAX)
kickUpdateButton.disabled = true;
else
kickUpdateButton.disabled = false;
}
function ValidateYouTubeDialog() {
// Get references to elements
const youtubeTitleCharLimit = document.getElementById('youtube-title-char-limit');
const youtubeTitleCharCount = document.getElementById('youtube-title-char-count');
const youtubeTitleCharMax = document.getElementById('youtube-title-char-max');
const youtubeDescriptionCharLimit = document.getElementById('youtube-description-char-limit');
const youtubeDescriptionCharCount = document.getElementById('youtube-description-char-count');
const youtubeDescriptionCharMax = document.getElementById('youtube-description-char-max');
const youtubeTagsCharLimit = document.getElementById('youtube-tags-char-limit');
const youtubeTagsCharCount = document.getElementById('youtube-tags-char-count');
const youtubeTagsCharMax = document.getElementById('youtube-tags-char-max');
const youtubeUpdateButton = document.getElementById('youtube-submit-button');
// Validate title field
const titleLength = youtubeTitleInput.value.length;
youtubeTitleCharCount.textContent = titleLength;
youtubeTitleCharMax.textContent = YOUTUBE_TITLE_MAX;
if (titleLength > YOUTUBE_TITLE_MAX)
youtubeTitleCharLimit.style.color = getComputedStyle(document.documentElement).getPropertyValue('--error-text-color');
else
youtubeTitleCharLimit.style.color = ""; // reset to default
// Validate description field
const descriptionLength = youtubeDescriptionInput.value.length;
youtubeDescriptionCharCount.textContent = descriptionLength;
youtubeDescriptionCharMax.textContent = YOUTUBE_DESCRIPTION_MAX;
if (descriptionLength > YOUTUBE_DESCRIPTION_MAX)
youtubeDescriptionCharLimit.style.color = getComputedStyle(document.documentElement).getPropertyValue('--error-text-color');
else
youtubeDescriptionCharLimit.style.color = ""; // reset to default
// Validate tags field
// Split by comma, trim each tag, sum lengths
const tagsArray = youtubeTagsInput.value
.split(',')
.map(tag => tag.trim())
.filter(tag => tag.length > 0);
const tagsLength = tagsArray.reduce((sum, tag) => sum + tag.length, 0) + (tagsArray.length > 0 ? tagsArray.length - 1 : 0);
youtubeTagsCharCount.textContent = tagsLength;
youtubeTagsCharMax.textContent = YOUTUBE_TAGS_MAX;
if (tagsLength > YOUTUBE_TAGS_MAX)
youtubeTagsCharLimit.style.color = getComputedStyle(document.documentElement).getPropertyValue('--error-text-color');
else
youtubeTagsCharLimit.style.color = ""; // reset to default
// Set button interactability (yes, that is a word — look it up)
if (titleLength > YOUTUBE_TITLE_MAX || descriptionLength > YOUTUBE_DESCRIPTION_MAX || tagsLength > YOUTUBE_TAGS_MAX)
youtubeUpdateButton.disabled = true;
else
youtubeUpdateButton.disabled = false;
}
// Attach event listener
allTitleInput.addEventListener("input", ValidateUpdateAllDialog);
twitchTitleInput.addEventListener("input", ValidateTwitchDialog);
twitchTagsInput.addEventListener("input", ValidateTwitchDialog);
kickTitleInput.addEventListener("input", ValidateKickDialog);
youtubeTitleInput.addEventListener("input", ValidateYouTubeDialog);
youtubeDescriptionInput.addEventListener("input", ValidateYouTubeDialog);
youtubeTagsInput.addEventListener("input", ValidateYouTubeDialog);
// Initial check (in case the input has prefilled text)
ValidateUpdateAllDialog();
ValidateTwitchDialog();
ValidateKickDialog();
ValidateYouTubeDialog();
////////////////////////////
@@ -25,6 +25,8 @@ button {
flex-direction: row;
align-items: center;
gap: 1em;
transition: 0.4s;
}
.broadcast-info {
@@ -73,4 +75,11 @@ button {
#youtube-warning {
display: none;
}
.char-limit {
margin-left: auto;
text-align: right;
font-size: 0.8em;
opacity: 0.7;
}