[mirotalksfu] - add Document PIP
هذا الالتزام موجود في:
@@ -31,6 +31,9 @@ const isIPadDevice = parserResult.device.model?.toLowerCase() === 'ipad';
|
||||
const isDesktopDevice = deviceType === 'desktop';
|
||||
const thisInfo = getInfo();
|
||||
|
||||
const isEmbedded = window.self !== window.top;
|
||||
const showDocumentPipBtn = !isEmbedded && 'documentPictureInPicture' in window;
|
||||
|
||||
const socket = io({
|
||||
transports: ['websocket'],
|
||||
reconnection: isDesktopDevice,
|
||||
@@ -412,6 +415,7 @@ function refreshMainButtonsToolTipPlacement() {
|
||||
setTippy('editorButton', 'Toggle the editor', placement);
|
||||
setTippy('transcriptionButton', 'Toggle transcription', placement);
|
||||
setTippy('whiteboardButton', 'Toggle the whiteboard', placement);
|
||||
setTippy('documentPiPButton', 'Toggle Document picture in picture', placement);
|
||||
setTippy('snapshotRoomButton', 'Snapshot screen, window, or tab', placement);
|
||||
setTippy('settingsButton', 'Toggle the settings', placement);
|
||||
setTippy('restartICEButton', 'Restart ICE', placement);
|
||||
@@ -1506,6 +1510,7 @@ function roomIsReady() {
|
||||
show(fullScreenButton);
|
||||
}
|
||||
BUTTONS.main.whiteboardButton && show(whiteboardButton);
|
||||
if (BUTTONS.main.documentPiPButton && showDocumentPipBtn) show(documentPiPButton);
|
||||
BUTTONS.main.settingsButton && show(settingsButton);
|
||||
isAudioAllowed ? show(stopAudioButton) : BUTTONS.main.startAudioButton && show(startAudioButton);
|
||||
isVideoAllowed ? show(stopVideoButton) : BUTTONS.main.startVideoButton && show(startVideoButton);
|
||||
@@ -2044,6 +2049,9 @@ function handleButtons() {
|
||||
whiteboardButton.onclick = () => {
|
||||
toggleWhiteboard();
|
||||
};
|
||||
documentPiPButton.onclick = () => {
|
||||
rc.toggleDocumentPIP();
|
||||
};
|
||||
snapshotRoomButton.onclick = () => {
|
||||
rc.snapshotRoom();
|
||||
};
|
||||
@@ -2872,6 +2880,17 @@ function handleSelects() {
|
||||
}
|
||||
whiteboardButton.click();
|
||||
break;
|
||||
case 'd':
|
||||
if (notPresenter && !BUTTONS.main.documentPiPButton) {
|
||||
userLog(
|
||||
'warning',
|
||||
'The presenter has disabled your ability to open the document PIP',
|
||||
'top-end',
|
||||
);
|
||||
break;
|
||||
}
|
||||
documentPiPButton.click();
|
||||
break;
|
||||
case 'j':
|
||||
if (notPresenter && !BUTTONS.main.emojiRoomButton) {
|
||||
userLog('warning', 'The presenter has disabled your ability to open the room emoji', 'top-end');
|
||||
|
||||
@@ -3512,6 +3512,134 @@ class RoomClient {
|
||||
}
|
||||
}
|
||||
|
||||
// ####################################################
|
||||
// HANDLE DOCUMENT PIP
|
||||
// ####################################################
|
||||
|
||||
async toggleDocumentPIP() {
|
||||
if (documentPictureInPicture.window) {
|
||||
documentPictureInPicture.window.close();
|
||||
console.log('DOCUMENT PIP close');
|
||||
return;
|
||||
}
|
||||
await this.documentPictureInPictureOpen();
|
||||
}
|
||||
|
||||
documentPictureInPictureClose() {
|
||||
if (!showDocumentPipBtn) return;
|
||||
if (documentPictureInPicture.window) {
|
||||
documentPictureInPicture.window.close();
|
||||
console.log('DOCUMENT PIP close');
|
||||
}
|
||||
}
|
||||
|
||||
async documentPictureInPictureOpen() {
|
||||
if (!showDocumentPipBtn) return;
|
||||
try {
|
||||
const pipWindow = await documentPictureInPicture.requestWindow({
|
||||
width: 300,
|
||||
height: 720,
|
||||
});
|
||||
|
||||
function updateCustomProperties() {
|
||||
const documentStyle = getComputedStyle(document.documentElement);
|
||||
|
||||
pipWindow.document.documentElement.style = `
|
||||
--body-bg: ${documentStyle.getPropertyValue('--body-bg')};
|
||||
`;
|
||||
}
|
||||
|
||||
updateCustomProperties();
|
||||
|
||||
const pipStylesheet = document.createElement('link');
|
||||
const pipVideoContainer = document.createElement('div');
|
||||
|
||||
pipStylesheet.type = 'text/css';
|
||||
pipStylesheet.rel = 'stylesheet';
|
||||
pipStylesheet.href = '../css/DocumentPiP.css';
|
||||
|
||||
pipVideoContainer.className = 'pipVideoContainer';
|
||||
|
||||
pipWindow.document.head.append(pipStylesheet);
|
||||
pipWindow.document.body.append(pipVideoContainer);
|
||||
|
||||
function cloneVideoElements() {
|
||||
let foundVideo = false;
|
||||
|
||||
pipVideoContainer.innerHTML = '';
|
||||
|
||||
[...document.querySelectorAll('video')].forEach((video) => {
|
||||
console.log('DOCUMENT PIP found video id -----> ' + video.id);
|
||||
|
||||
// No video stream detected or is video share from URL...
|
||||
if (!video.srcObject || video.id === '__videoShare') return;
|
||||
|
||||
let videoPIPAllowed = false;
|
||||
|
||||
// get video element
|
||||
const videoPlayer = rc.getId(video.id);
|
||||
|
||||
// Check if video can be add on pipVideo
|
||||
if ([rc.videoProducerId, rc.screenProducerId].includes(video.id)) {
|
||||
// PRODUCER
|
||||
videoPIPAllowed = !videoPlayer.classList.contains('videoCircle'); // not in privacy mode
|
||||
console.log('DOCUMENT PIP PRODUCER videoPIPAllowed -----> ' + videoPIPAllowed);
|
||||
} else {
|
||||
// CONSUMER
|
||||
videoPIPAllowed = !videoPlayer.classList.contains('videoCircle'); // not in privacy mode
|
||||
console.log('DOCUMENT PIP CONAUMER videoPIPAllowed -----> ' + videoPIPAllowed);
|
||||
}
|
||||
|
||||
if (!videoPIPAllowed) return;
|
||||
|
||||
// Video is ON not in privacy mode continue....
|
||||
|
||||
foundVideo = true;
|
||||
|
||||
const pipVideo = document.createElement('video');
|
||||
|
||||
pipVideo.classList.add('pipVideo');
|
||||
pipVideo.classList.toggle('mirror', video.classList.contains('mirror'));
|
||||
pipVideo.srcObject = video.srcObject;
|
||||
pipVideo.autoplay = true;
|
||||
pipVideo.muted = true;
|
||||
|
||||
pipVideoContainer.append(pipVideo);
|
||||
});
|
||||
|
||||
return foundVideo;
|
||||
}
|
||||
|
||||
if (!cloneVideoElements()) {
|
||||
rc.documentPictureInPictureClose();
|
||||
return userLog('warning', 'No video allowed for Document PIP', 'top-end', 6000);
|
||||
}
|
||||
|
||||
const videoObserver = new MutationObserver(() => {
|
||||
cloneVideoElements();
|
||||
});
|
||||
|
||||
videoObserver.observe(rc.videoMediaContainer, {
|
||||
childList: true,
|
||||
});
|
||||
|
||||
const documentObserver = new MutationObserver(() => {
|
||||
updateCustomProperties();
|
||||
});
|
||||
|
||||
documentObserver.observe(document.documentElement, {
|
||||
attributeFilter: ['style'],
|
||||
});
|
||||
|
||||
pipWindow.addEventListener('unload', () => {
|
||||
videoObserver.disconnect();
|
||||
documentObserver.disconnect();
|
||||
});
|
||||
} catch (err) {
|
||||
userLog('warning', err.message, 'top-end', 6000);
|
||||
}
|
||||
}
|
||||
|
||||
// ####################################################
|
||||
// FULL SCREEN
|
||||
// ####################################################
|
||||
|
||||
@@ -23,10 +23,11 @@ let BUTTONS = {
|
||||
swapCameraButton: true,
|
||||
chatButton: true,
|
||||
pollButton: true,
|
||||
editorButton: true,
|
||||
raiseHandButton: true,
|
||||
transcriptionButton: true,
|
||||
whiteboardButton: true,
|
||||
editorButton: true,
|
||||
documentPiPButton: true,
|
||||
snapshotRoomButton: true,
|
||||
emojiRoomButton: true,
|
||||
settingsButton: true,
|
||||
@@ -240,6 +241,7 @@ function handleRulesBroadcasting() {
|
||||
BUTTONS.main.swapCameraButton = false;
|
||||
//BUTTONS.main.raiseHandButton = false;
|
||||
BUTTONS.main.whiteboardButton = false;
|
||||
BUTTONS.main.documentPiPButton = false;
|
||||
//BUTTONS.main.snapshotRoomButton = false;
|
||||
//BUTTONS.main.emojiRoomButton = false,
|
||||
//BUTTONS.main.pollButton = false;
|
||||
@@ -275,6 +277,7 @@ function handleRulesBroadcasting() {
|
||||
elemDisplay('swapCameraButton', false);
|
||||
//elemDisplay('raiseHandButton', false);
|
||||
elemDisplay('whiteboardButton', false);
|
||||
elemDisplay('documentPiPButton', false);
|
||||
//elemDisplay('snapshotRoomButton', false);
|
||||
//elemDisplay('emojiRoomButton', false);
|
||||
//elemDisplay('pollButton', false);
|
||||
|
||||
المرجع في مشكلة جديدة
حظر مستخدم