diff --git a/multichat-overlay/script.js b/multichat-overlay/script.js
index 859c983..139ba9f 100644
--- a/multichat-overlay/script.js
+++ b/multichat-overlay/script.js
@@ -10,6 +10,12 @@ const sbServerPort = urlParams.get("port") || "8080";
const avatarMap = new Map();
const pronounMap = new Map();
+/////////////////
+// GLOBAL VARS //
+/////////////////
+
+const youtubeRegex = /^(?:https?:\/\/)?(?:www\.)?(?:youtube\.com\/(?:watch\?v=|embed\/|shorts\/)|youtu\.be\/)([a-zA-Z0-9_-]{11})(?:[&?#].*)?$/;
+
/////////////
// OPTIONS //
/////////////
@@ -37,6 +43,7 @@ const scrollDirection = GetIntParam("scrollDirection", 1);
const groupConsecutiveMessages = GetBooleanParam("groupConsecutiveMessages", false);
const inlineChat = GetBooleanParam("inlineChat", false);
const imageEmbedPermissionLevel = GetIntParam("imageEmbedPermissionLevel", 20);
+const showYouTubeLinkPreviews = GetBooleanParam("showYouTubeLinkPreviews", true);
const showTwitchMessages = GetBooleanParam("showTwitchMessages", true);
const showTwitchAnnouncements = GetBooleanParam("showTwitchAnnouncements", true);
@@ -537,7 +544,7 @@ async function TwitchChatMessage(data) {
// Render emotes
for (i in data.emotes) {
- const emoteElement = `
`;
+ const emoteElement = `
`;
const emoteName = EscapeRegExp(data.emotes[i].name);
let regexPattern = emoteName;
@@ -560,7 +567,7 @@ async function TwitchChatMessage(data) {
const bits = data.cheerEmotes[i].bits;
const imageUrl = data.cheerEmotes[i].imageUrl;
const name = data.cheerEmotes[i].name;
- const cheerEmoteElement = `
`;
+ const cheerEmoteElement = `
`;
const bitsElements = `${bits}`
messageDiv.innerHTML = messageDiv.innerHTML.replace(new RegExp(`\\b${name}${bits}\\b`, 'i'), cheerEmoteElement + bitsElements);
}
@@ -609,6 +616,15 @@ async function TwitchChatMessage(data) {
else {
AddMessageItem(instance, data.message.msgId, 'twitch', data.user.id);
}
+
+ // Render YouTube links
+ if (youtubeRegex.test(message))
+ {
+ const videoId = ExtractYouTubeVideoId(message);
+ const videoData = await GetYouTubeVideoData(videoId);
+
+ YouTubeThumbnailPreview(videoData);
+ }
}
async function TwitchAutomaticRewardRedemption(data) {
@@ -738,7 +754,7 @@ async function TwitchAnnouncement(data) {
// Render emotes
for (i in data.parts) {
if (data.parts[i].type == `emote`) {
- const emoteElement = `
`;
+ const emoteElement = `
`;
const emoteName = EscapeRegExp(data.parts[i].text);
let regexPattern = emoteName;
@@ -1081,7 +1097,7 @@ function TwitchChatCleared(data) {
}
}
-function YouTubeMessage(data) {
+async function YouTubeMessage(data) {
if (!showYouTubeMessages)
return;
@@ -1193,7 +1209,7 @@ function YouTubeMessage(data) {
// Render emotes
for (i in data.emotes) {
- const emoteElement = `
`;
+ const emoteElement = `
`;
// messageDiv.innerHTML = messageDiv.innerHTML.replace(new RegExp(`\\b${data.emotes[i].name}\\b`), emoteElement);
messageDiv.innerHTML = messageDiv.innerHTML.replace(data.emotes[i].name, emoteElement);
}
@@ -1240,6 +1256,15 @@ function YouTubeMessage(data) {
else {
AddMessageItem(instance, data.eventId, 'youtube', data.user.id);
}
+
+ // Render YouTube links
+ if (youtubeRegex.test(data.message))
+ {
+ const videoId = ExtractYouTubeVideoId(data.message);
+ const videoData = await GetYouTubeVideoData(videoId);
+
+ YouTubeThumbnailPreview(videoData);
+ }
}
function YouTubeSuperChat(data) {
@@ -1670,7 +1695,6 @@ function FourthwallOrderPlaced(data) {
const iconDiv = instance.querySelector("#icon");
const titleDiv = instance.querySelector("#title");
const contentDiv = instance.querySelector("#content");
- const messageContentsDiv = instance.querySelector(".message-contents");
// Set the card background colors
cardDiv.classList.add('fourthwall');
@@ -1680,7 +1704,7 @@ function FourthwallOrderPlaced(data) {
// titleDiv.classList.add('centerThatShitHomie');
// contentDiv.classList.add('centerThatShitHomie');
- messageContentsDiv.style.gap = '1em';
+ avatarDiv.style.width = 'auto';
// Set the text
let user = data.username;
@@ -2171,6 +2195,15 @@ async function KickChatMessage(data) {
else {
AddMessageItem(instance, data.msgId, 'kick', data.userId);
}
+
+ // Render YouTube links
+ if (youtubeRegex.test(message))
+ {
+ const videoId = ExtractYouTubeVideoId(message);
+ const videoData = await GetYouTubeVideoData(videoId);
+
+ YouTubeThumbnailPreview(videoData);
+ }
}
async function KickFollow(data) {
@@ -2446,7 +2479,7 @@ function KickBan(data) {
-function TikTokChat(data) {
+async function TikTokChat(data) {
// Don't post messages starting with "!"
if (data.comment.startsWith("!") && excludeCommands)
return;
@@ -2571,6 +2604,15 @@ function TikTokChat(data) {
}
AddMessageItem(instance, data.msgId, 'tiktok', data.userId);
+
+ // Render YouTube links
+ if (youtubeRegex.test(message))
+ {
+ const videoId = ExtractYouTubeVideoId(message);
+ const videoData = await GetYouTubeVideoData(videoId);
+
+ YouTubeThumbnailPreview(videoData);
+ }
}
function TikTokGift(data) {
@@ -2631,6 +2673,41 @@ function TikTokSubscribe(data) {
AddMessageItem(instance, data.msgId);
}
+function YouTubeThumbnailPreview(data) {
+ if (!showYouTubeLinkPreviews)
+ return;
+
+ // Get a reference to the template
+ const template = document.getElementById('cardTemplate');
+
+ // Create a new instance of the template
+ const instance = template.content.cloneNode(true);
+
+ // Get divs
+ const cardDiv = instance.querySelector("#card");
+ const headerDiv = instance.querySelector("#header");
+ const avatarDiv = instance.querySelector("#avatar");
+ const iconDiv = instance.querySelector("#icon");
+ const titleDiv = instance.querySelector("#title");
+ const contentDiv = instance.querySelector("#content");
+
+ // Set the card background colors
+ cardDiv.classList.add('thumbnail');
+
+ avatarDiv.style.width = 'auto';
+
+ // Set the text
+ const title = data.title;
+ const author = data.author;
+ const thumbnail = `
`;
+
+ avatarDiv.innerHTML = thumbnail;
+ titleDiv.innerHTML = `${title}`;
+ contentDiv.innerHTML = `by ${author}`;
+
+ AddMessageItem(instance);
+}
+
//////////////////////
@@ -2753,6 +2830,11 @@ function IsImageUrl(url) {
}
}
+function ExtractYouTubeVideoId(url) {
+ const match = url.match(youtubeRegex);
+ return match ? match[1] : null;
+}
+
function AddMessageItem(element, elementID, platform, userId) {
// Calculate the height of the div before inserting
const tempDiv = document.getElementById('IPutThisHereSoICanCalculateHowBigEachMessageIsSupposedToBeBeforeIAddItToTheMessageList');
@@ -2972,4 +3054,35 @@ function SetConnectionStatus(connected) {
statusContainer.style.transition = "";
statusContainer.style.opacity = 1;
}
-}
\ No newline at end of file
+}
+
+async function GetYouTubeVideoData(videoId) {
+ const url = `https://www.youtube.com/oembed?url=https://www.youtube.com/watch?v=${videoId}&format=json`;
+
+ try {
+ const response = await fetch(url);
+ if (!response.ok) {
+ throw new Error(`HTTP error! Status: ${response.status}`);
+ }
+
+ const data = await response.json();
+
+ return {
+ title: data.title,
+ author: data.author_name,
+ thumbnail: data.thumbnail_url,
+ };
+ } catch (error) {
+ console.error('Error fetching YouTube video data:', error);
+ return null;
+ }
+}
+
+async function SemenLover()
+{
+ let data = await GetYouTubeVideoData('dQw4w9WgXcQ');
+
+ console.log(data);
+}
+
+SemenLover()
\ No newline at end of file
diff --git a/multichat-overlay/settings/settings.json b/multichat-overlay/settings/settings.json
index 31f8a69..e4f8777 100644
--- a/multichat-overlay/settings/settings.json
+++ b/multichat-overlay/settings/settings.json
@@ -226,6 +226,14 @@
"defaultValue": 20,
"group": "General"
},
+ {
+ "id": "showYouTubeLinkPreviews",
+ "label": "Show YouTube Link Previews",
+ "description": "",
+ "type": "checkbox",
+ "defaultValue": true,
+ "group": "General"
+ },
{
"id": "showTwitchMessages",
"label": "Chat Messages",
diff --git a/multichat-overlay/style.css b/multichat-overlay/style.css
index fc7fc7f..9f36798 100644
--- a/multichat-overlay/style.css
+++ b/multichat-overlay/style.css
@@ -168,6 +168,12 @@ li {
transform: translate(0px, 0.25em);
}
+.youtubeThumbnail {
+ width: 5em;
+ border-radius: 0.25em;
+ transform: translate(0px, 0.25em);
+}
+
/* .centerThatShitHomie {
display: inline-block;
width: 100%;
@@ -319,6 +325,15 @@ li {
background: linear-gradient(#262626BF, #000000BF) !important;
}
+.fourthwall {
+ background: linear-gradient(#466edbbf, #1c56f5bf) !important;
+}
+
+.thumbnail {
+ background: linear-gradient(#494949bf, #202020bf) !important;
+}
+
+
.blank {
background: #adadad00 !important;
}