[mirotalksfu] - #194 Add Virtual Background Image from URL
هذا الالتزام موجود في:
@@ -58,7 +58,7 @@ dev dependencies: {
|
||||
* @license For commercial or closed source, contact us at license.mirotalk@gmail.com or purchase directly via CodeCanyon
|
||||
* @license CodeCanyon: https://codecanyon.net/item/mirotalk-sfu-webrtc-realtime-video-conferences/40769970
|
||||
* @author Miroslav Pejic - miroslav.pejic.85@gmail.com
|
||||
* @version 1.7.37
|
||||
* @version 1.7.38
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mirotalksfu",
|
||||
"version": "1.7.37",
|
||||
"version": "1.7.38",
|
||||
"description": "WebRTC SFU browser-based video calls",
|
||||
"main": "Server.js",
|
||||
"scripts": {
|
||||
|
||||
@@ -269,6 +269,54 @@ body {
|
||||
color: white;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------
|
||||
# Custom modal to paste image URL
|
||||
--------------------------------------------------------------*/
|
||||
|
||||
.imageUrlModal {
|
||||
display: none;
|
||||
position: fixed;
|
||||
z-index: 9999;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background: var(--body-bg);
|
||||
width: 80%;
|
||||
max-width: 400px;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.imageUrlModal-content {
|
||||
background: var(--body-bg);
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.imageUrlModal input {
|
||||
width: 80%;
|
||||
padding: 10px;
|
||||
margin: 10px 0;
|
||||
border: var(--border);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.imageUrlModal button {
|
||||
padding: 10px 20px;
|
||||
border: none;
|
||||
background: var(--btns-bg-color);
|
||||
border-radius: 10px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.imageUrlModal button:hover {
|
||||
background: var(--body-bg) !important;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------
|
||||
# Buttons bar
|
||||
--------------------------------------------------------------*/
|
||||
|
||||
ثنائية
public/images/link.png
Normal file
ثنائية
public/images/link.png
Normal file
ملف ثنائي غير معروض.
|
بعد العرض: | الارتفاع: | الحجم: 2.4 KiB |
@@ -64,7 +64,7 @@ let BRAND = {
|
||||
},
|
||||
about: {
|
||||
imageUrl: '../images/mirotalk-logo.gif',
|
||||
title: '<strong>WebRTC SFU v1.7.37</strong>',
|
||||
title: '<strong>WebRTC SFU v1.7.38</strong>',
|
||||
html: `
|
||||
<button
|
||||
id="support-button"
|
||||
|
||||
@@ -5116,6 +5116,74 @@ function showImageSelector() {
|
||||
imageGrid.appendChild(uploadImg);
|
||||
setTippy(uploadImg.id, 'Upload your custom background', 'top');
|
||||
|
||||
// Function to fetch image from URL and store it in IndexedDB
|
||||
async function fetchAndStoreImage(url) {
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
const blob = await response.blob();
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onload = function (e) {
|
||||
const imgData = e.target.result;
|
||||
saveImageToIndexedDB(imgData);
|
||||
addImageToUI(imgData);
|
||||
};
|
||||
|
||||
reader.readAsDataURL(blob);
|
||||
} catch (error) {
|
||||
console.error('Error fetching image:', error);
|
||||
}
|
||||
}
|
||||
|
||||
saveImageUrlBtn.addEventListener('click', async () => {
|
||||
elemDisplay(imageUrlModal.id, false);
|
||||
if (isValidURL(imageUrlInput.value)) {
|
||||
await fetchAndStoreImage(imageUrlInput.value);
|
||||
imageUrlInput.value = '';
|
||||
}
|
||||
});
|
||||
|
||||
cancelImageUrlBtn.addEventListener('click', () => {
|
||||
elemDisplay(imageUrlModal.id, false);
|
||||
imageUrlInput.value = '';
|
||||
});
|
||||
|
||||
function askForImageURL() {
|
||||
elemDisplay(imageUrlModal.id, true);
|
||||
|
||||
// Take URL from clipboard ex:
|
||||
navigator.clipboard
|
||||
.readText()
|
||||
.then((clipboardText) => {
|
||||
if (!clipboardText) return false;
|
||||
const sanitizedText = filterXSS(clipboardText);
|
||||
if (isValidURL(sanitizedText) && imageUrlInput) {
|
||||
imageUrlInput.value = sanitizedText;
|
||||
}
|
||||
return false;
|
||||
})
|
||||
.catch(() => {
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
// Function to validate URL format
|
||||
function isValidURL(url) {
|
||||
return (
|
||||
url.match(/\.(jpeg|jpg|png|gif|webp|bmp|svg|apng|avif|heif|heic|tiff?|ico|cur|jfif|pjpeg|pjp|raw)$/i) !==
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
// Create link to upload image
|
||||
const linkImg = document.createElement('img');
|
||||
linkImg.id = 'linkImage';
|
||||
linkImg.src = image.link;
|
||||
linkImg.alt = 'Link image';
|
||||
linkImg.addEventListener('click', askForImageURL);
|
||||
imageGrid.appendChild(linkImg);
|
||||
setTippy(linkImg.id, 'Upload Image from Url', 'top');
|
||||
|
||||
// Loop through virtual background images dynamically
|
||||
virtualBackgrounds.forEach((imageUrl, index) => {
|
||||
const img = document.createElement('img');
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
* @license For commercial or closed source, contact us at license.mirotalk@gmail.com or purchase directly via CodeCanyon
|
||||
* @license CodeCanyon: https://codecanyon.net/item/mirotalk-sfu-webrtc-realtime-video-conferences/40769970
|
||||
* @author Miroslav Pejic - miroslav.pejic.85@gmail.com
|
||||
* @version 1.7.37
|
||||
* @version 1.7.38
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -114,6 +114,7 @@ const image = {
|
||||
transcription: '../images/transcription.png',
|
||||
back: '../images/back.png',
|
||||
blur: '../images/blur.png',
|
||||
link: '../images/link.png',
|
||||
upload: '../images/upload.png',
|
||||
virtualBackground: {
|
||||
one: '../images/virtual-background/background-1.jpg',
|
||||
@@ -1837,6 +1838,75 @@ class RoomClient {
|
||||
imageGridVideo.appendChild(uploadImg);
|
||||
setTippy(uploadImg.id, 'Upload your custom background', 'top');
|
||||
|
||||
// Function to fetch image from URL and store it in IndexedDB
|
||||
async function fetchAndStoreImage(url) {
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
const blob = await response.blob();
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onload = function (e) {
|
||||
const imgData = e.target.result;
|
||||
saveImageToIndexedDB(imgData);
|
||||
addImageToUI(imgData);
|
||||
};
|
||||
|
||||
reader.readAsDataURL(blob);
|
||||
} catch (error) {
|
||||
console.error('Error fetching image:', error);
|
||||
}
|
||||
}
|
||||
|
||||
saveImageUrlBtn.addEventListener('click', async () => {
|
||||
elemDisplay(imageUrlModal.id, false);
|
||||
if (isValidURL(imageUrlInput.value)) {
|
||||
await fetchAndStoreImage(imageUrlInput.value);
|
||||
imageUrlInput.value = '';
|
||||
}
|
||||
});
|
||||
|
||||
cancelImageUrlBtn.addEventListener('click', () => {
|
||||
elemDisplay(imageUrlModal.id, false);
|
||||
imageUrlInput.value = '';
|
||||
});
|
||||
|
||||
function askForImageURL() {
|
||||
elemDisplay(imageUrlModal.id, true);
|
||||
|
||||
// Take URL from clipboard ex:
|
||||
navigator.clipboard
|
||||
.readText()
|
||||
.then((clipboardText) => {
|
||||
if (!clipboardText) return false;
|
||||
const sanitizedText = filterXSS(clipboardText);
|
||||
if (isValidURL(sanitizedText) && imageUrlInput) {
|
||||
imageUrlInput.value = sanitizedText;
|
||||
}
|
||||
return false;
|
||||
})
|
||||
.catch(() => {
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
// Function to validate URL format
|
||||
function isValidURL(url) {
|
||||
return (
|
||||
url.match(
|
||||
/\.(jpeg|jpg|png|gif|webp|bmp|svg|apng|avif|heif|heic|tiff?|ico|cur|jfif|pjpeg|pjp|raw)$/i,
|
||||
) !== null
|
||||
);
|
||||
}
|
||||
|
||||
// Create link to upload image
|
||||
const linkImg = document.createElement('img');
|
||||
linkImg.id = 'linkImage';
|
||||
linkImg.src = image.link;
|
||||
linkImg.alt = 'Link image';
|
||||
linkImg.addEventListener('click', askForImageURL);
|
||||
imageGridVideo.appendChild(linkImg);
|
||||
setTippy(linkImg.id, 'Upload Image from Url', 'top');
|
||||
|
||||
// Loop through virtual background images dynamically
|
||||
virtualBackgrounds.forEach((imageUrl, index) => {
|
||||
const img = document.createElement('img');
|
||||
|
||||
@@ -175,6 +175,14 @@ access to use this app.
|
||||
</div>
|
||||
</div>
|
||||
<div id="usernameEmoji" class="usernameEmoji fadein center hidden"></div>
|
||||
<div id="imageUrlModal" class="imageUrlModal">
|
||||
<div class="imageUrlModal-content fadein">
|
||||
<h2>Paste Image URL</h2>
|
||||
<input type="text" id="imageUrlInput" placeholder="Enter the image URL here..." />
|
||||
<button id="saveImageUrlBtn">Save</button>
|
||||
<button id="cancelImageUrlBtn">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div id="control" class="fadein">
|
||||
|
||||
المرجع في مشكلة جديدة
حظر مستخدم