[mirotalksfu] - add YouTube share video
هذا الالتزام موجود في:
@@ -20,6 +20,7 @@ Powered by `WebRTC` with [SFU](https://mediasoup.org) integrated server.
|
|||||||
- Collaborative whiteboard for the teachers
|
- Collaborative whiteboard for the teachers
|
||||||
- Select Microphone - Speaker and Video source
|
- Select Microphone - Speaker and Video source
|
||||||
- Recording your Screen, Audio or Video
|
- Recording your Screen, Audio or Video
|
||||||
|
- Share any YouTube video in real time to your participants
|
||||||
- Full Screen Mode on mouse click on the Video element
|
- Full Screen Mode on mouse click on the Video element
|
||||||
- Supports [REST API](api/README.md) (Application Programming Interface)
|
- Supports [REST API](api/README.md) (Application Programming Interface)
|
||||||
|
|
||||||
|
|||||||
@@ -139,6 +139,14 @@ access to use this app.
|
|||||||
<button id="startScreenButton" class="hidden"><i class="fas fa-desktop"></i> Start screen</button>
|
<button id="startScreenButton" class="hidden"><i class="fas fa-desktop"></i> Start screen</button>
|
||||||
<button id="stopScreenButton" class="hidden"><i class="fas fa-stop-circle"></i> Stop screen</button>
|
<button id="stopScreenButton" class="hidden"><i class="fas fa-stop-circle"></i> Stop screen</button>
|
||||||
<button id="fileShareButton" class="hidden"><i class="fas fa-folder-open"></i> File Sharing</button>
|
<button id="fileShareButton" class="hidden"><i class="fas fa-folder-open"></i> File Sharing</button>
|
||||||
|
<div class="dropdown">
|
||||||
|
<button id="youTubeShareButton" class="hidden"><i class="fab fa-youtube"></i> YouTube</button>
|
||||||
|
<div id="youTubeSettings" class="dropdown-content fadein">
|
||||||
|
<div id="youTubeOptions">
|
||||||
|
<button id="youTubeCloseBtn"><i class="fas fa-times"></i> Close Video</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="dropdown">
|
<div class="dropdown">
|
||||||
<button id="whiteboardButton" class="hidden">
|
<button id="whiteboardButton" class="hidden">
|
||||||
<i class="fas fa-chalkboard-teacher"></i> Whiteboard
|
<i class="fas fa-chalkboard-teacher"></i> Whiteboard
|
||||||
|
|||||||
@@ -611,6 +611,20 @@ progress {
|
|||||||
min-width: 100%;
|
min-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------
|
||||||
|
# YouTube Video
|
||||||
|
--------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#youTubeSettings {
|
||||||
|
display: 'none';
|
||||||
|
position: relative;
|
||||||
|
background: var(--body-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#youTubeCloseBtn:hover {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
/*--------------------------------------------------------------
|
/*--------------------------------------------------------------
|
||||||
# Whiteboard
|
# Whiteboard
|
||||||
--------------------------------------------------------------*/
|
--------------------------------------------------------------*/
|
||||||
|
|||||||
ثنائية
public/images/youtube.png
Normal file
ثنائية
public/images/youtube.png
Normal file
ملف ثنائي غير معروض.
|
بعد العرض: | الارتفاع: | الحجم: 2.4 KiB |
@@ -434,6 +434,7 @@ function roomIsReady() {
|
|||||||
show(raiseHandButton);
|
show(raiseHandButton);
|
||||||
if (isAudioAllowed) show(startAudioButton);
|
if (isAudioAllowed) show(startAudioButton);
|
||||||
if (isVideoAllowed) show(startVideoButton);
|
if (isVideoAllowed) show(startVideoButton);
|
||||||
|
show(youTubeShareButton);
|
||||||
show(fileShareButton);
|
show(fileShareButton);
|
||||||
show(whiteboardButton);
|
show(whiteboardButton);
|
||||||
show(participantsButton);
|
show(participantsButton);
|
||||||
@@ -612,6 +613,12 @@ function handleButtons() {
|
|||||||
fileShareButton.onclick = () => {
|
fileShareButton.onclick = () => {
|
||||||
rc.selectFileToShare();
|
rc.selectFileToShare();
|
||||||
};
|
};
|
||||||
|
youTubeShareButton.onclick = () => {
|
||||||
|
rc.youTubeShareVideo();
|
||||||
|
};
|
||||||
|
youTubeCloseBtn.onclick = () => {
|
||||||
|
rc.closeYouTube(true);
|
||||||
|
};
|
||||||
sendAbortBtn.onclick = () => {
|
sendAbortBtn.onclick = () => {
|
||||||
rc.abortFileTransfer();
|
rc.abortFileTransfer();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ const image = {
|
|||||||
locked: '../images/locked.png',
|
locked: '../images/locked.png',
|
||||||
mute: '../images/mute.png',
|
mute: '../images/mute.png',
|
||||||
hide: '../images/hide.png',
|
hide: '../images/hide.png',
|
||||||
|
youtube: '../images/youtube.png',
|
||||||
};
|
};
|
||||||
|
|
||||||
const mediaType = {
|
const mediaType = {
|
||||||
@@ -458,6 +459,13 @@ class RoomClient {
|
|||||||
}.bind(this),
|
}.bind(this),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.socket.on(
|
||||||
|
'youTubeAction',
|
||||||
|
function (data) {
|
||||||
|
this.youTubeAction(data);
|
||||||
|
}.bind(this),
|
||||||
|
);
|
||||||
|
|
||||||
this.socket.on(
|
this.socket.on(
|
||||||
'fileAbort',
|
'fileAbort',
|
||||||
function (data) {
|
function (data) {
|
||||||
@@ -1857,6 +1865,117 @@ class RoomClient {
|
|||||||
return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[i];
|
return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ####################################################
|
||||||
|
// YOUTUBE SHARE VIDEO
|
||||||
|
// ####################################################
|
||||||
|
|
||||||
|
youTubeShareVideo() {
|
||||||
|
rc.sound('open');
|
||||||
|
|
||||||
|
Swal.fire({
|
||||||
|
background: swalBackground,
|
||||||
|
position: 'center',
|
||||||
|
imageUrl: image.youtube,
|
||||||
|
title: 'Share YouTube Video',
|
||||||
|
text: 'Past YouTube video URL',
|
||||||
|
input: 'text',
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonText: `Share`,
|
||||||
|
showClass: {
|
||||||
|
popup: 'animate__animated animate__fadeInDown',
|
||||||
|
},
|
||||||
|
hideClass: {
|
||||||
|
popup: 'animate__animated animate__fadeOutUp',
|
||||||
|
},
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.value) {
|
||||||
|
if (!this.thereIsParticipants()) {
|
||||||
|
userLog('info', 'No participants detected', 'top-end');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// https://www.youtube.com/watch?v=RT6_Id5-7-s
|
||||||
|
|
||||||
|
let you_tube_url = this.getYoutubeEmbed(result.value);
|
||||||
|
if (you_tube_url) {
|
||||||
|
let data = {
|
||||||
|
peer_name: this.peer_name,
|
||||||
|
you_tube_url: you_tube_url,
|
||||||
|
action: 'open',
|
||||||
|
};
|
||||||
|
console.log('YouTube video URL: ', you_tube_url);
|
||||||
|
this.socket.emit('youTubeAction', data);
|
||||||
|
this.openYouTube(data);
|
||||||
|
} else {
|
||||||
|
this.userLog('error', 'Not valid YouTube URL', 'top-end');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getYoutubeEmbed(url) {
|
||||||
|
let regExp = /^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#&?]*).*/;
|
||||||
|
let match = url.match(regExp);
|
||||||
|
return match && match[7].length == 11 ? 'https://www.youtube.com/embed/' + match[7] + '?autoplay=1' : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
youTubeAction(data) {
|
||||||
|
let action = data.action;
|
||||||
|
switch (action) {
|
||||||
|
case 'open':
|
||||||
|
this.userLog('info', `${peer_name} <i class="fab fa-youtube"></i> opened the YouTube video`, 'top-end');
|
||||||
|
this.openYouTube(data);
|
||||||
|
break;
|
||||||
|
case 'close':
|
||||||
|
this.userLog('info', `${peer_name} <i class="fab fa-youtube"></i> closed the YouTube video`, 'top-end');
|
||||||
|
this.closeYouTube();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
openYouTube(data) {
|
||||||
|
let d, iframe;
|
||||||
|
let peer_name = data.peer_name;
|
||||||
|
let you_tube_url = data.you_tube_url;
|
||||||
|
this.closeYouTube();
|
||||||
|
youTubeSettings.style.display = 'block';
|
||||||
|
d = document.createElement('div');
|
||||||
|
d.className = 'Camera';
|
||||||
|
d.id = '__youTube';
|
||||||
|
iframe = document.createElement('iframe');
|
||||||
|
iframe.setAttribute('id', '__youTubeIframe');
|
||||||
|
iframe.setAttribute('title', peer_name);
|
||||||
|
iframe.setAttribute('width', '100%');
|
||||||
|
iframe.setAttribute('height', '100%');
|
||||||
|
iframe.setAttribute('src', you_tube_url);
|
||||||
|
iframe.setAttribute(
|
||||||
|
'allow',
|
||||||
|
'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture',
|
||||||
|
);
|
||||||
|
iframe.setAttribute('frameborder', '0');
|
||||||
|
iframe.setAttribute('allowfullscreen', true);
|
||||||
|
d.appendChild(iframe);
|
||||||
|
this.videoMediaContainer.appendChild(d);
|
||||||
|
resizeVideoMedia();
|
||||||
|
rc.sound('joined');
|
||||||
|
}
|
||||||
|
|
||||||
|
closeYouTube(emit = false) {
|
||||||
|
if (emit) {
|
||||||
|
let data = {
|
||||||
|
peer_name: this.peer_name,
|
||||||
|
action: 'close',
|
||||||
|
};
|
||||||
|
this.socket.emit('youTubeAction', data);
|
||||||
|
}
|
||||||
|
let youTubeDiv = this.getId('__youTube');
|
||||||
|
if (youTubeDiv) {
|
||||||
|
youTubeSettings.style.display = 'none';
|
||||||
|
youTubeDiv.parentNode.removeChild(youTubeDiv);
|
||||||
|
resizeVideoMedia();
|
||||||
|
rc.sound('left');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ####################################################
|
// ####################################################
|
||||||
// ROOM ACTION
|
// ROOM ACTION
|
||||||
// ####################################################
|
// ####################################################
|
||||||
|
|||||||
@@ -117,8 +117,8 @@
|
|||||||
</h2>
|
</h2>
|
||||||
<p class="section-paragraph mb-0">
|
<p class="section-paragraph mb-0">
|
||||||
MiroTalk with SFU integrated Server. We engineered a platform with maximum video
|
MiroTalk with SFU integrated Server. We engineered a platform with maximum video
|
||||||
quality lowest latency that makes your calls crystal clear. Desktop and Mobile
|
quality lowest latency that makes your calls crystal clear. Compatible with Desktop
|
||||||
compatible!
|
and Mobile devices!
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ async function ngrokStart() {
|
|||||||
tunnel: tunnel,
|
tunnel: tunnel,
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Ngrok Start error: ', err);
|
log.error('Ngrok Start error: ', err);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -192,7 +192,7 @@ async function createWorkers() {
|
|||||||
rtcMaxPort: config.mediasoup.worker.rtcMaxPort,
|
rtcMaxPort: config.mediasoup.worker.rtcMaxPort,
|
||||||
});
|
});
|
||||||
worker.on('died', () => {
|
worker.on('died', () => {
|
||||||
console.error('Mediasoup worker died, exiting in 2 seconds... [pid:%d]', worker.pid);
|
log.error('Mediasoup worker died, exiting in 2 seconds... [pid:%d]', worker.pid);
|
||||||
setTimeout(() => process.exit(1), 2000);
|
setTimeout(() => process.exit(1), 2000);
|
||||||
});
|
});
|
||||||
workers.push(worker);
|
workers.push(worker);
|
||||||
@@ -265,6 +265,11 @@ io.on('connection', (socket) => {
|
|||||||
roomList.get(socket.room_id).broadCast(socket.id, 'fileAbort', data);
|
roomList.get(socket.room_id).broadCast(socket.id, 'fileAbort', data);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
socket.on('youTubeAction', (data) => {
|
||||||
|
log.debug('YouTube: ', data);
|
||||||
|
roomList.get(socket.room_id).broadCast(socket.id, 'youTubeAction', data);
|
||||||
|
});
|
||||||
|
|
||||||
socket.on('wbCanvasToJson', (data) => {
|
socket.on('wbCanvasToJson', (data) => {
|
||||||
// let objLength = bytesToSize(Object.keys(data).length);
|
// let objLength = bytesToSize(Object.keys(data).length);
|
||||||
// log.debug('Send Whiteboard canvas JSON', { length: objLength });
|
// log.debug('Send Whiteboard canvas JSON', { length: objLength });
|
||||||
@@ -328,7 +333,7 @@ io.on('connection', (socket) => {
|
|||||||
const { params } = await roomList.get(socket.room_id).createWebRtcTransport(socket.id);
|
const { params } = await roomList.get(socket.room_id).createWebRtcTransport(socket.id);
|
||||||
callback(params);
|
callback(params);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Create WebRtc Transport error: ', err);
|
log.error('Create WebRtc Transport error: ', err);
|
||||||
callback({
|
callback({
|
||||||
error: err.message,
|
error: err.message,
|
||||||
});
|
});
|
||||||
|
|||||||
المرجع في مشكلة جديدة
حظر مستخدم