[mirotalksfu] - #194 Keep uploaded virtual background images in IndexedDB

هذا الالتزام موجود في:
Miroslav Pejic
2025-02-14 18:10:52 +01:00
الأصل 81d3ae87e4
التزام ac6f9733a5
6 ملفات معدلة مع 232 إضافات و25 حذوفات

عرض الملف

@@ -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 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 * @license CodeCanyon: https://codecanyon.net/item/mirotalk-sfu-webrtc-realtime-video-conferences/40769970
* @author Miroslav Pejic - miroslav.pejic.85@gmail.com * @author Miroslav Pejic - miroslav.pejic.85@gmail.com
* @version 1.7.36 * @version 1.7.37
* *
*/ */

عرض الملف

@@ -1,6 +1,6 @@
{ {
"name": "mirotalksfu", "name": "mirotalksfu",
"version": "1.7.36", "version": "1.7.37",
"description": "WebRTC SFU browser-based video calls", "description": "WebRTC SFU browser-based video calls",
"main": "Server.js", "main": "Server.js",
"scripts": { "scripts": {
@@ -79,7 +79,7 @@
"mediasoup-client": "3.8.1", "mediasoup-client": "3.8.1",
"ngrok": "^5.0.0-beta.2", "ngrok": "^5.0.0-beta.2",
"nodemailer": "^6.10.0", "nodemailer": "^6.10.0",
"openai": "^4.85.0", "openai": "^4.85.1",
"qs": "6.14.0", "qs": "6.14.0",
"sanitize-filename": "^1.6.3", "sanitize-filename": "^1.6.3",
"socket.io": "4.8.1", "socket.io": "4.8.1",

عرض الملف

@@ -216,7 +216,7 @@ body {
} }
.image-grid img:hover { .image-grid img:hover {
transform: scale(1.1); opacity: 0.5;
box-shadow: 0px 10px 20px rgba(0, 0, 0, 0.3); box-shadow: 0px 10px 20px rgba(0, 0, 0, 0.3);
} }
@@ -242,6 +242,33 @@ body {
} }
} }
/*--------------------------------------------------------------
# Custom image delete icon
--------------------------------------------------------------*/
.image-wrapper {
position: relative;
display: inline-block;
}
.delete-icon {
position: absolute;
right: 3px;
font-size: 12px;
cursor: pointer;
color: #f5f5f5b0;
border-radius: 5px;
padding: 5px;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
}
.delete-icon:hover {
background: var(--body-bg);
color: white;
}
/*-------------------------------------------------------------- /*--------------------------------------------------------------
# Buttons bar # Buttons bar
--------------------------------------------------------------*/ --------------------------------------------------------------*/

عرض الملف

@@ -64,7 +64,7 @@ let BRAND = {
}, },
about: { about: {
imageUrl: '../images/mirotalk-logo.gif', imageUrl: '../images/mirotalk-logo.gif',
title: '<strong>WebRTC SFU v1.7.36</strong>', title: '<strong>WebRTC SFU v1.7.37</strong>',
html: ` html: `
<button <button
id="support-button" id="support-button"

عرض الملف

@@ -5011,20 +5011,107 @@ function showImageSelector() {
if (file) { if (file) {
const reader = new FileReader(); const reader = new FileReader();
reader.onload = function (e) { reader.onload = function (e) {
const customImg = document.createElement('img'); const imgData = e.target.result;
customImg.id = `customImage${imageCounter}`; // Assign a unique id saveImageToIndexedDB(imgData);
customImg.src = e.target.result; addImageToUI(imgData);
customImg.alt = 'Custom Background';
customImg.addEventListener('click', async function () {
await applyVirtualBackground(initVideo, initStream, false, e.target.result);
});
imageGrid.appendChild(customImg);
setTippy(customImg.id, 'Custom background', 'top');
}; };
reader.readAsDataURL(file); reader.readAsDataURL(file);
} }
}); });
// Load images from IndexedDB
async function loadStoredImages() {
const storedImages = await getImagesFromIndexedDB();
storedImages.forEach((imgData) => addImageToUI(imgData));
}
// Save image to IndexedDB
async function saveImageToIndexedDB(imgData) {
const db = await openDB();
const transaction = db.transaction('images', 'readwrite');
const store = transaction.objectStore('images');
store.add({ imgData });
}
// Open IndexedDB
function openDB() {
return new Promise((resolve, reject) => {
const request = indexedDB.open('customImageDB', 1);
request.onupgradeneeded = function (event) {
const db = event.target.result;
if (!db.objectStoreNames.contains('images')) {
db.createObjectStore('images', { keyPath: 'id', autoIncrement: true });
}
};
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
// Get images from IndexedDB
function getImagesFromIndexedDB() {
return new Promise((resolve, reject) => {
const dbRequest = openDB();
dbRequest.then((db) => {
const transaction = db.transaction('images', 'readonly');
const store = transaction.objectStore('images');
const request = store.getAll();
request.onsuccess = () => resolve(request.result.map((item) => item.imgData));
request.onerror = () => reject(request.error);
});
});
}
// Add image to UI
function addImageToUI(imgData) {
const imageContainer = document.createElement('div');
imageContainer.className = 'image-wrapper';
const customImg = document.createElement('img');
customImg.id = `customImageGrid${imageCounter}`;
customImg.src = imgData;
customImg.alt = 'Custom Background';
customImg.addEventListener('click', async function () {
await applyVirtualBackground(initVideo, initStream, false, imgData);
});
// Create delete button
const deleteBtn = createDeleteIcon(imgData, imageContainer);
imageContainer.appendChild(customImg);
imageContainer.appendChild(deleteBtn);
imageGrid.appendChild(imageContainer);
setTippy(customImg.id, 'Custom background', 'top');
imageCounter++;
}
// Create delete icon
function createDeleteIcon(imgData, container) {
const deleteIcon = document.createElement('span');
deleteIcon.className = 'delete-icon fas fa-times';
deleteIcon.addEventListener('click', function (event) {
event.stopPropagation(); // Prevent triggering background change
removeImageFromIndexedDB(imgData);
container.remove();
});
return deleteIcon;
}
// Remove image from IndexedDB
async function removeImageFromIndexedDB(imgData) {
const db = await openDB();
const transaction = db.transaction('images', 'readwrite');
const store = transaction.objectStore('images');
const request = store.getAll();
request.onsuccess = () => {
const items = request.result;
const itemToDelete = items.find((item) => item.imgData === imgData);
if (itemToDelete) store.delete(itemToDelete.id);
};
}
// Append the button to the imageGrid // Append the button to the imageGrid
imageGrid.appendChild(uploadImg); imageGrid.appendChild(uploadImg);
setTippy(uploadImg.id, 'Upload your custom background', 'top'); setTippy(uploadImg.id, 'Upload your custom background', 'top');
@@ -5040,6 +5127,9 @@ function showImageSelector() {
}); });
imageGrid.appendChild(img); imageGrid.appendChild(img);
}); });
// Load images from IndexedDB on page load
loadStoredImages();
} }
async function applyVirtualBackground(videoElement, stream, blurLevel, backgroundImage) { async function applyVirtualBackground(videoElement, stream, blurLevel, backgroundImage) {

عرض الملف

@@ -9,7 +9,7 @@
* @license For commercial or closed source, contact us at license.mirotalk@gmail.com or purchase directly via CodeCanyon * @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 * @license CodeCanyon: https://codecanyon.net/item/mirotalk-sfu-webrtc-realtime-video-conferences/40769970
* @author Miroslav Pejic - miroslav.pejic.85@gmail.com * @author Miroslav Pejic - miroslav.pejic.85@gmail.com
* @version 1.7.36 * @version 1.7.37
* *
*/ */
@@ -1685,7 +1685,7 @@ class RoomClient {
const cleanVbImg = document.createElement('img'); const cleanVbImg = document.createElement('img');
cleanVbImg.id = 'cleanVbImg'; cleanVbImg.id = 'cleanVbImg';
cleanVbImg.src = image.user; cleanVbImg.src = image.user;
cleanVbImg.alt = 'Clean virtual bacgroundk'; cleanVbImg.alt = 'Clean virtual background';
cleanVbImg.dataset.index = 'cleanVb'; cleanVbImg.dataset.index = 'cleanVb';
cleanVbImg.addEventListener('click', async function () { cleanVbImg.addEventListener('click', async function () {
if (!virtualBackgroundBlurLevel && !virtualBackgroundSelectedImage) { if (!virtualBackgroundBlurLevel && !virtualBackgroundSelectedImage) {
@@ -1726,26 +1726,113 @@ class RoomClient {
fileInput.click(); fileInput.click();
}); });
// Load images from IndexedDB
async function loadStoredImages() {
const storedImages = await getImagesFromIndexedDB();
storedImages.forEach((imgData) => addImageToUI(imgData));
}
// Handle image selection // Handle image selection
fileInput.addEventListener('change', function () { fileInput.addEventListener('change', function () {
const file = this.files[0]; const file = this.files[0];
if (file) { if (file) {
const reader = new FileReader(); const reader = new FileReader();
reader.onload = function (e) { reader.onload = function (e) {
const customImg = document.createElement('img'); const imgData = e.target.result;
customImg.id = `customImage${imageCounter}`; // Assign a unique id saveImageToIndexedDB(imgData);
customImg.src = e.target.result; addImageToUI(imgData);
customImg.alt = 'Custom Background';
customImg.addEventListener('click', async function () {
await rc.applyVirtualBackground(false, e.target.result);
});
imageGridVideo.appendChild(customImg);
setTippy(customImg.id, 'Custom background', 'top');
}; };
reader.readAsDataURL(file); reader.readAsDataURL(file);
} }
}); });
// Add image with delete button to UI
function addImageToUI(imgData) {
const imageContainer = document.createElement('div');
imageContainer.className = 'image-wrapper';
const customImg = document.createElement('img');
customImg.id = `customImageGrid${imageCounter}`;
customImg.src = imgData;
customImg.alt = 'Custom Background';
customImg.addEventListener('click', async function () {
await rc.applyVirtualBackground(false, imgData);
});
// Create delete button
const deleteBtn = createDeleteIcon(imgData, imageContainer);
imageContainer.appendChild(customImg);
imageContainer.appendChild(deleteBtn);
imageGridVideo.appendChild(imageContainer);
setTippy(customImg.id, 'Custom background', 'top');
imageCounter++;
}
// Create delete icon
function createDeleteIcon(imgData, container) {
const deleteIcon = document.createElement('span');
deleteIcon.className = 'delete-icon fas fa-times';
deleteIcon.addEventListener('click', function (event) {
event.stopPropagation(); // Prevent triggering background change
removeImageFromIndexedDB(imgData);
container.remove();
});
return deleteIcon;
}
// Save image to IndexedDB
async function saveImageToIndexedDB(imgData) {
const db = await openDB();
const transaction = db.transaction('images', 'readwrite');
const store = transaction.objectStore('images');
store.add({ imgData });
}
// Open IndexedDB
function openDB() {
return new Promise((resolve, reject) => {
const request = indexedDB.open('customImageDB', 1);
request.onupgradeneeded = function (event) {
const db = event.target.result;
if (!db.objectStoreNames.contains('images')) {
db.createObjectStore('images', { keyPath: 'id', autoIncrement: true });
}
};
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
// Get images from IndexedDB
function getImagesFromIndexedDB() {
return new Promise((resolve, reject) => {
const dbRequest = openDB();
dbRequest.then((db) => {
const transaction = db.transaction('images', 'readonly');
const store = transaction.objectStore('images');
const request = store.getAll();
request.onsuccess = () => resolve(request.result.map((item) => item.imgData));
request.onerror = () => reject(request.error);
});
});
}
// Remove image from IndexedDB
async function removeImageFromIndexedDB(imgData) {
const db = await openDB();
const transaction = db.transaction('images', 'readwrite');
const store = transaction.objectStore('images');
const request = store.getAll();
request.onsuccess = () => {
const items = request.result;
const itemToDelete = items.find((item) => item.imgData === imgData);
if (itemToDelete) store.delete(itemToDelete.id);
};
}
// Append the button to the imageGrid // Append the button to the imageGrid
imageGridVideo.appendChild(uploadImg); imageGridVideo.appendChild(uploadImg);
setTippy(uploadImg.id, 'Upload your custom background', 'top'); setTippy(uploadImg.id, 'Upload your custom background', 'top');
@@ -1761,6 +1848,9 @@ class RoomClient {
}); });
imageGridVideo.appendChild(img); imageGridVideo.appendChild(img);
}); });
// Load images from IndexedDB on page load
loadStoredImages();
} }
async applyVirtualBackground(blurLevel, backgroundImage) { async applyVirtualBackground(blurLevel, backgroundImage) {