mirror of
https://github.com/nuttylmao/nutty.gg.git
synced 2026-09-18 19:50:58 -04:00
Add support for YouTube Link Previews
This commit is contained in:
+122
-9
@@ -10,6 +10,12 @@ const sbServerPort = urlParams.get("port") || "8080";
|
|||||||
const avatarMap = new Map();
|
const avatarMap = new Map();
|
||||||
const pronounMap = 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 //
|
// OPTIONS //
|
||||||
/////////////
|
/////////////
|
||||||
@@ -37,6 +43,7 @@ const scrollDirection = GetIntParam("scrollDirection", 1);
|
|||||||
const groupConsecutiveMessages = GetBooleanParam("groupConsecutiveMessages", false);
|
const groupConsecutiveMessages = GetBooleanParam("groupConsecutiveMessages", false);
|
||||||
const inlineChat = GetBooleanParam("inlineChat", false);
|
const inlineChat = GetBooleanParam("inlineChat", false);
|
||||||
const imageEmbedPermissionLevel = GetIntParam("imageEmbedPermissionLevel", 20);
|
const imageEmbedPermissionLevel = GetIntParam("imageEmbedPermissionLevel", 20);
|
||||||
|
const showYouTubeLinkPreviews = GetBooleanParam("showYouTubeLinkPreviews", true);
|
||||||
|
|
||||||
const showTwitchMessages = GetBooleanParam("showTwitchMessages", true);
|
const showTwitchMessages = GetBooleanParam("showTwitchMessages", true);
|
||||||
const showTwitchAnnouncements = GetBooleanParam("showTwitchAnnouncements", true);
|
const showTwitchAnnouncements = GetBooleanParam("showTwitchAnnouncements", true);
|
||||||
@@ -537,7 +544,7 @@ async function TwitchChatMessage(data) {
|
|||||||
|
|
||||||
// Render emotes
|
// Render emotes
|
||||||
for (i in data.emotes) {
|
for (i in data.emotes) {
|
||||||
const emoteElement = `<img src="${data.emotes[i].imageUrl}" class="emote"/>`;
|
const emoteElement = `<img src="${data.emotes[i].imageUrl}" title="${data.emotes[i].name}" class="emote"/>`;
|
||||||
const emoteName = EscapeRegExp(data.emotes[i].name);
|
const emoteName = EscapeRegExp(data.emotes[i].name);
|
||||||
|
|
||||||
let regexPattern = emoteName;
|
let regexPattern = emoteName;
|
||||||
@@ -560,7 +567,7 @@ async function TwitchChatMessage(data) {
|
|||||||
const bits = data.cheerEmotes[i].bits;
|
const bits = data.cheerEmotes[i].bits;
|
||||||
const imageUrl = data.cheerEmotes[i].imageUrl;
|
const imageUrl = data.cheerEmotes[i].imageUrl;
|
||||||
const name = data.cheerEmotes[i].name;
|
const name = data.cheerEmotes[i].name;
|
||||||
const cheerEmoteElement = `<img src="${imageUrl}" class="emote"/>`;
|
const cheerEmoteElement = `<img src="${imageUrl}" title="${data.cheerEmotes[i].name} class="emote"/>`;
|
||||||
const bitsElements = `<span class="bits">${bits}</span>`
|
const bitsElements = `<span class="bits">${bits}</span>`
|
||||||
messageDiv.innerHTML = messageDiv.innerHTML.replace(new RegExp(`\\b${name}${bits}\\b`, 'i'), cheerEmoteElement + bitsElements);
|
messageDiv.innerHTML = messageDiv.innerHTML.replace(new RegExp(`\\b${name}${bits}\\b`, 'i'), cheerEmoteElement + bitsElements);
|
||||||
}
|
}
|
||||||
@@ -609,6 +616,15 @@ async function TwitchChatMessage(data) {
|
|||||||
else {
|
else {
|
||||||
AddMessageItem(instance, data.message.msgId, 'twitch', data.user.id);
|
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) {
|
async function TwitchAutomaticRewardRedemption(data) {
|
||||||
@@ -738,7 +754,7 @@ async function TwitchAnnouncement(data) {
|
|||||||
// Render emotes
|
// Render emotes
|
||||||
for (i in data.parts) {
|
for (i in data.parts) {
|
||||||
if (data.parts[i].type == `emote`) {
|
if (data.parts[i].type == `emote`) {
|
||||||
const emoteElement = `<img src="${data.parts[i].imageUrl}" class="emote"/>`;
|
const emoteElement = `<img src="${data.parts[i].imageUrl}" title="${data.parts[i].text}" class="emote"/>`;
|
||||||
const emoteName = EscapeRegExp(data.parts[i].text);
|
const emoteName = EscapeRegExp(data.parts[i].text);
|
||||||
|
|
||||||
let regexPattern = emoteName;
|
let regexPattern = emoteName;
|
||||||
@@ -1081,7 +1097,7 @@ function TwitchChatCleared(data) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function YouTubeMessage(data) {
|
async function YouTubeMessage(data) {
|
||||||
if (!showYouTubeMessages)
|
if (!showYouTubeMessages)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -1193,7 +1209,7 @@ function YouTubeMessage(data) {
|
|||||||
|
|
||||||
// Render emotes
|
// Render emotes
|
||||||
for (i in data.emotes) {
|
for (i in data.emotes) {
|
||||||
const emoteElement = `<img src="${data.emotes[i].imageUrl}" class="emote"/>`;
|
const emoteElement = `<img src="${data.emotes[i].imageUrl}" title="${data.emotes[i].name}" class="emote"/>`;
|
||||||
// messageDiv.innerHTML = messageDiv.innerHTML.replace(new RegExp(`\\b${data.emotes[i].name}\\b`), 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);
|
messageDiv.innerHTML = messageDiv.innerHTML.replace(data.emotes[i].name, emoteElement);
|
||||||
}
|
}
|
||||||
@@ -1240,6 +1256,15 @@ function YouTubeMessage(data) {
|
|||||||
else {
|
else {
|
||||||
AddMessageItem(instance, data.eventId, 'youtube', data.user.id);
|
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) {
|
function YouTubeSuperChat(data) {
|
||||||
@@ -1670,7 +1695,6 @@ function FourthwallOrderPlaced(data) {
|
|||||||
const iconDiv = instance.querySelector("#icon");
|
const iconDiv = instance.querySelector("#icon");
|
||||||
const titleDiv = instance.querySelector("#title");
|
const titleDiv = instance.querySelector("#title");
|
||||||
const contentDiv = instance.querySelector("#content");
|
const contentDiv = instance.querySelector("#content");
|
||||||
const messageContentsDiv = instance.querySelector(".message-contents");
|
|
||||||
|
|
||||||
// Set the card background colors
|
// Set the card background colors
|
||||||
cardDiv.classList.add('fourthwall');
|
cardDiv.classList.add('fourthwall');
|
||||||
@@ -1680,7 +1704,7 @@ function FourthwallOrderPlaced(data) {
|
|||||||
// titleDiv.classList.add('centerThatShitHomie');
|
// titleDiv.classList.add('centerThatShitHomie');
|
||||||
// contentDiv.classList.add('centerThatShitHomie');
|
// contentDiv.classList.add('centerThatShitHomie');
|
||||||
|
|
||||||
messageContentsDiv.style.gap = '1em';
|
avatarDiv.style.width = 'auto';
|
||||||
|
|
||||||
// Set the text
|
// Set the text
|
||||||
let user = data.username;
|
let user = data.username;
|
||||||
@@ -2171,6 +2195,15 @@ async function KickChatMessage(data) {
|
|||||||
else {
|
else {
|
||||||
AddMessageItem(instance, data.msgId, 'kick', data.userId);
|
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) {
|
async function KickFollow(data) {
|
||||||
@@ -2446,7 +2479,7 @@ function KickBan(data) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
function TikTokChat(data) {
|
async function TikTokChat(data) {
|
||||||
// Don't post messages starting with "!"
|
// Don't post messages starting with "!"
|
||||||
if (data.comment.startsWith("!") && excludeCommands)
|
if (data.comment.startsWith("!") && excludeCommands)
|
||||||
return;
|
return;
|
||||||
@@ -2571,6 +2604,15 @@ function TikTokChat(data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
AddMessageItem(instance, data.msgId, 'tiktok', data.userId);
|
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) {
|
function TikTokGift(data) {
|
||||||
@@ -2631,6 +2673,41 @@ function TikTokSubscribe(data) {
|
|||||||
AddMessageItem(instance, data.msgId);
|
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 = `<img src="${data.thumbnail}" class="youtubeThumbnail"/>`;
|
||||||
|
|
||||||
|
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) {
|
function AddMessageItem(element, elementID, platform, userId) {
|
||||||
// Calculate the height of the div before inserting
|
// Calculate the height of the div before inserting
|
||||||
const tempDiv = document.getElementById('IPutThisHereSoICanCalculateHowBigEachMessageIsSupposedToBeBeforeIAddItToTheMessageList');
|
const tempDiv = document.getElementById('IPutThisHereSoICanCalculateHowBigEachMessageIsSupposedToBeBeforeIAddItToTheMessageList');
|
||||||
@@ -2972,4 +3054,35 @@ function SetConnectionStatus(connected) {
|
|||||||
statusContainer.style.transition = "";
|
statusContainer.style.transition = "";
|
||||||
statusContainer.style.opacity = 1;
|
statusContainer.style.opacity = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
@@ -226,6 +226,14 @@
|
|||||||
"defaultValue": 20,
|
"defaultValue": 20,
|
||||||
"group": "General"
|
"group": "General"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "showYouTubeLinkPreviews",
|
||||||
|
"label": "Show YouTube Link Previews",
|
||||||
|
"description": "",
|
||||||
|
"type": "checkbox",
|
||||||
|
"defaultValue": true,
|
||||||
|
"group": "General"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "showTwitchMessages",
|
"id": "showTwitchMessages",
|
||||||
"label": "Chat Messages",
|
"label": "Chat Messages",
|
||||||
|
|||||||
@@ -168,6 +168,12 @@ li {
|
|||||||
transform: translate(0px, 0.25em);
|
transform: translate(0px, 0.25em);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.youtubeThumbnail {
|
||||||
|
width: 5em;
|
||||||
|
border-radius: 0.25em;
|
||||||
|
transform: translate(0px, 0.25em);
|
||||||
|
}
|
||||||
|
|
||||||
/* .centerThatShitHomie {
|
/* .centerThatShitHomie {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -319,6 +325,15 @@ li {
|
|||||||
background: linear-gradient(#262626BF, #000000BF) !important;
|
background: linear-gradient(#262626BF, #000000BF) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fourthwall {
|
||||||
|
background: linear-gradient(#466edbbf, #1c56f5bf) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbnail {
|
||||||
|
background: linear-gradient(#494949bf, #202020bf) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.blank {
|
.blank {
|
||||||
background: #adadad00 !important;
|
background: #adadad00 !important;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user