diff --git a/.common/styles/global.css b/.common/styles/global.css
index 7b1de30..988fc25 100644
--- a/.common/styles/global.css
+++ b/.common/styles/global.css
@@ -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 {
diff --git a/multistream-title-updater/config.json b/multistream-title-updater/config.json
index ccac738..a6578a3 100644
--- a/multistream-title-updater/config.json
+++ b/multistream-title-updater/config.json
@@ -14,5 +14,5 @@
"name": "Multistream Title Updater | Open Link"
}
],
- "sbImportCode": "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"
+ "sbImportCode": "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"
}
\ No newline at end of file
diff --git a/multistream-title-updater/contents/icons/platforms/youtube-shorts.png b/multistream-title-updater/contents/icons/platforms/youtube-shorts.png
new file mode 100644
index 0000000..54d2745
Binary files /dev/null and b/multistream-title-updater/contents/icons/platforms/youtube-shorts.png differ
diff --git a/multistream-title-updater/contents/index.html b/multistream-title-updater/contents/index.html
index 723cb49..0bb65b6 100644
--- a/multistream-title-updater/contents/index.html
+++ b/multistream-title-updater/contents/index.html
@@ -8,7 +8,7 @@
-
+
@@ -20,7 +20,7 @@
-

+
-
-
+
+
-
+
-
+
-
-
+
+
+
+ Tag too long • / Tags
+
+
+
+
-
+
-
+
-
+
-
-
+
@@ -152,17 +206,20 @@
-
![]()
+
-
+
diff --git a/multistream-title-updater/contents/script.js b/multistream-title-updater/contents/script.js
index 3e3f77b..07bf804 100644
--- a/multistream-title-updater/contents/script.js
+++ b/multistream-title-updater/contents/script.js
@@ -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();
+
+
////////////////////////////
diff --git a/multistream-title-updater/contents/style.css b/multistream-title-updater/contents/style.css
index f6d4159..3e28841 100644
--- a/multistream-title-updater/contents/style.css
+++ b/multistream-title-updater/contents/style.css
@@ -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;
}
\ No newline at end of file