[mirotalksfu] #212 - add maximize/minimize button

هذا الالتزام موجود في:
Miroslav Pejic
2025-05-26 15:20:26 +02:00
الأصل ad9332f529
التزام 2d469e7c06
6 ملفات معدلة مع 86 إضافات و17 حذوفات

عرض الملف

@@ -64,7 +64,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.8.48 * @version 1.8.49
* *
*/ */

عرض الملف

@@ -1,6 +1,6 @@
{ {
"name": "mirotalksfu", "name": "mirotalksfu",
"version": "1.8.48", "version": "1.8.49",
"description": "WebRTC SFU browser-based video calls", "description": "WebRTC SFU browser-based video calls",
"main": "Server.js", "main": "Server.js",
"scripts": { "scripts": {

عرض الملف

@@ -162,7 +162,10 @@
.videoMenuBarShare { .videoMenuBarShare {
z-index: 2; z-index: 2;
position: absolute; position: absolute;
display: inline; display: flex;
flex-direction: row-reverse;
align-items: center;
gap: 1px;
top: 0; top: 0;
left: 0; left: 0;
padding: 10px; padding: 10px;

عرض الملف

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

عرض الملف

@@ -11,7 +11,7 @@ if (location.href.substr(0, 5) !== 'https') location.href = 'https' + location.h
* @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.8.48 * @version 1.8.49
* *
*/ */
@@ -5445,7 +5445,7 @@ function showAbout() {
position: 'center', position: 'center',
imageUrl: BRAND.about?.imageUrl && BRAND.about.imageUrl.trim() !== '' ? BRAND.about.imageUrl : image.about, imageUrl: BRAND.about?.imageUrl && BRAND.about.imageUrl.trim() !== '' ? BRAND.about.imageUrl : image.about,
customClass: { image: 'img-about' }, customClass: { image: 'img-about' },
title: BRAND.about?.title && BRAND.about.title.trim() !== '' ? BRAND.about.title : 'WebRTC SFU v1.8.48', title: BRAND.about?.title && BRAND.about.title.trim() !== '' ? BRAND.about.title : 'WebRTC SFU v1.8.49',
html: ` html: `
<br /> <br />
<div id="about"> <div id="about">
@@ -5487,4 +5487,4 @@ function showAbout() {
showClass: { popup: 'animate__animated animate__fadeInDown' }, showClass: { popup: 'animate__animated animate__fadeInDown' },
hideClass: { popup: 'animate__animated animate__fadeOutUp' }, hideClass: { popup: 'animate__animated animate__fadeOutUp' },
}); });
} }

عرض الملف

@@ -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.8.48 * @version 1.8.49
* *
*/ */
@@ -383,7 +383,7 @@ class RoomClient {
this.recSyncChunkSize = 1000000; // 1MB this.recSyncChunkSize = 1000000; // 1MB
// Encodings // Encodings
this.preferLocalCodecsOrder = false; // Prefer local codecs order this.preferLocalCodecsOrder = false; // Prefer local codecs order
this.forceVP8 = false; // Force VP8 codec for webcam and screen sharing this.forceVP8 = false; // Force VP8 codec for webcam and screen sharing
this.forceVP9 = false; // Force VP9 codec for webcam and screen sharing this.forceVP9 = false; // Force VP9 codec for webcam and screen sharing
this.forceH264 = false; // Force H264 codec for webcam and screen sharing this.forceH264 = false; // Force H264 codec for webcam and screen sharing
@@ -749,11 +749,11 @@ class RoomClient {
try { try {
await device.load({ await device.load({
routerRtpCapabilities, routerRtpCapabilities,
preferLocalCodecsOrder: !!this.preferLocalCodecsOrder preferLocalCodecsOrder: !!this.preferLocalCodecsOrder,
}); });
console.log( console.log(
`Device loaded successfully with router RTP capabilities (preferLocalCodecsOrder: ${!!this.preferLocalCodecsOrder})`, `Device loaded successfully with router RTP capabilities (preferLocalCodecsOrder: ${!!this.preferLocalCodecsOrder})`,
device.rtpCapabilities device.rtpCapabilities,
); );
} catch (error) { } catch (error) {
console.error('Error loading device with router RTP capabilities:', error); console.error('Error loading device with router RTP capabilities:', error);
@@ -5324,8 +5324,8 @@ class RoomClient {
if (!input || typeof input !== 'string') return false; if (!input || typeof input !== 'string') return false;
try { try {
const url = new URL(input); const url = new URL(input);
return ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp', '.tiff', '.svg'].some(ext => return ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp', '.tiff', '.svg'].some((ext) =>
url.pathname.toLowerCase().endsWith(ext) url.pathname.toLowerCase().endsWith(ext),
); );
} catch (e) { } catch (e) {
return false; return false;
@@ -7147,7 +7147,7 @@ class RoomClient {
} }
openVideo(data) { openVideo(data) {
let d, vb, e, video, pn; let d, vb, e, video, pn, fsBtn;
let peer_name = data.peer_name; let peer_name = data.peer_name;
let video_url = data.video_url + (this.isMobileSafari ? '&enablejsapi=1&mute=1' : ''); // Safari need user interaction let video_url = data.video_url + (this.isMobileSafari ? '&enablejsapi=1&mute=1' : ''); // Safari need user interaction
let is_youtube = data.is_youtube; let is_youtube = data.is_youtube;
@@ -7162,6 +7162,8 @@ class RoomClient {
vb.className = 'videoMenuBarShare fadein'; vb.className = 'videoMenuBarShare fadein';
e = this.createButton('__videoExit', 'fas fa-times'); e = this.createButton('__videoExit', 'fas fa-times');
pn = this.createButton('__pinUnpin', html.pin); pn = this.createButton('__pinUnpin', html.pin);
fsBtn = this.createButton('__videoFS', html.fullScreen);
if (is_youtube) { if (is_youtube) {
video = document.createElement('iframe'); video = document.createElement('iframe');
video.setAttribute('title', peer_name); video.setAttribute('title', peer_name);
@@ -7179,20 +7181,17 @@ class RoomClient {
allowEscapeKey: false, allowEscapeKey: false,
background: swalBackground, background: swalBackground,
position: 'top', position: 'top',
// icon: 'info',
imageUrl: image.videoShare, imageUrl: image.videoShare,
title: 'Unmute Video', title: 'Unmute Video',
text: 'Tap the button below to unmute and play the video with sound.', text: 'Tap the button below to unmute and play the video with sound.',
confirmButtonText: 'Unmute', confirmButtonText: 'Unmute',
didOpen: () => { didOpen: () => {
// Focus on the button when the popup opens
const unmuteButton = Swal.getConfirmButton(); const unmuteButton = Swal.getConfirmButton();
if (unmuteButton) unmuteButton.focus(); if (unmuteButton) unmuteButton.focus();
}, },
}).then((result) => { }).then((result) => {
if (result.isConfirmed) { if (result.isConfirmed) {
if (video && video.contentWindow) { if (video && video.contentWindow) {
// Unmute the video and play
video.contentWindow.postMessage('{"event":"command","func":"unMute","args":""}', '*'); video.contentWindow.postMessage('{"event":"command","func":"unMute","args":""}', '*');
video.contentWindow.postMessage('{"event":"command","func":"playVideo","args":""}', '*'); video.contentWindow.postMessage('{"event":"command","func":"playVideo","args":""}', '*');
} }
@@ -7213,11 +7212,77 @@ class RoomClient {
video.setAttribute('width', '100%'); video.setAttribute('width', '100%');
video.setAttribute('height', '100%'); video.setAttribute('height', '100%');
vb.appendChild(e); vb.appendChild(e);
vb.appendChild(fsBtn);
if (!this.isMobileDevice) vb.appendChild(pn); if (!this.isMobileDevice) vb.appendChild(pn);
d.appendChild(video); d.appendChild(video);
d.appendChild(vb); d.appendChild(vb);
this.videoMediaContainer.appendChild(d); this.videoMediaContainer.appendChild(d);
fsBtn.addEventListener('click', () => {
// Try to use the Fullscreen API
if (
video.requestFullscreen ||
video.webkitRequestFullscreen ||
video.mozRequestFullScreen ||
video.msRequestFullscreen
) {
this.isFullScreen() ? this.goOutFullscreen(video) : this.goInFullscreen(video);
} else {
elemDisplay('__videoFS', false);
// Maximize video with CSS
video.style.position = 'fixed';
video.style.top = 0;
video.style.left = 0;
video.style.width = '100vw';
video.style.height = '100vh';
video.style.zIndex = 9999;
// Add a close/maximize button for fallback
let isMaximized = true;
const closeBtn = document.createElement('button');
closeBtn.innerText = isMaximized ? 'Minimize' : 'Maximize';
closeBtn.style.position = 'absolute';
closeBtn.style.top = '1px';
closeBtn.style.left = '1px';
closeBtn.style.zIndex = 10000;
closeBtn.style.background = 'rgba(0,0,0,0.5)';
closeBtn.style.color = '#fff';
closeBtn.style.border = 'none';
closeBtn.style.padding = '8px 12px';
closeBtn.style.borderRadius = '4px';
closeBtn.style.cursor = 'pointer';
closeBtn.onclick = () => {
if (isMaximized) {
video.style.position = '';
video.style.top = '';
video.style.left = '';
video.style.width = '';
video.style.height = '';
video.style.zIndex = '';
closeBtn.innerText = 'Maximize';
isMaximized = false;
} else {
video.style.position = 'fixed';
video.style.top = 0;
video.style.left = 0;
video.style.width = '100vw';
video.style.height = '100vh';
video.style.zIndex = 9999;
closeBtn.innerText = 'Minimize';
isMaximized = true;
}
};
// Ensure only one button is added
if (!video.parentNode.querySelector('.mobile-video-close-btn')) {
closeBtn.classList.add('mobile-video-close-btn');
video.parentNode.appendChild(closeBtn);
}
}
});
const exitVideoBtn = this.getId(e.id); const exitVideoBtn = this.getId(e.id);
exitVideoBtn.addEventListener('click', (e) => { exitVideoBtn.addEventListener('click', (e) => {
e.preventDefault(); e.preventDefault();
@@ -7231,6 +7296,7 @@ class RoomClient {
if (!this.isMobileDevice) { if (!this.isMobileDevice) {
this.setTippy(pn.id, 'Toggle Pin video player', 'bottom'); this.setTippy(pn.id, 'Toggle Pin video player', 'bottom');
this.setTippy(e.id, 'Close video player', 'bottom'); this.setTippy(e.id, 'Close video player', 'bottom');
this.setTippy(fsBtn.id, 'Full screen', 'bottom');
} }
handleAspectRatio(); handleAspectRatio();