[mirotalksfu] - first release

هذا الالتزام موجود في:
Miroslav Pejic
2021-08-24 12:24:22 +02:00
الأصل a8c3fa853b
التزام 55572c8210
59 ملفات معدلة مع 21260 إضافات و1 حذوفات

734
public/js/Room.js Normal file
عرض الملف

@@ -0,0 +1,734 @@
'use strict';
if (location.href.substr(0, 5) !== 'https') location.href = 'https' + location.href.substr(4, location.href.length - 4);
const RoomURL = window.location.href;
const swalBackground = 'rgba(0, 0, 0, 0.7)';
const swalImageUrl = '../images/pricing-illustration.svg';
const url = {
ipLookup: 'https://extreme-ip-lookup.com/json/',
};
let rc = null;
let producer = null;
let peer_name = 'peer_' + getRandomNumber(5);
let peer_geo = null;
let peer_info = null;
let room_id = location.pathname.substring(6);
let isEnumerateDevices = false;
let isAudioAllowed = false;
let isVideoAllowed = false;
let isAudioOn = true;
let isVideoOn = true;
let recTimer = null;
let recElapsedTime = null;
const socket = io();
function getRandomNumber(length) {
let result = '';
let characters = '0123456789';
let charactersLength = characters.length;
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}
function initClient() {
if (!DetectRTC.isMobileDevice) {
setTippy('sessionTime', 'Session time', 'bottom');
setTippy('exitButton', 'Exit room', 'bottom');
setTippy('shareButton', 'Share Room', 'bottom');
setTippy('devicesButton', 'Devices', 'bottom');
setTippy('chatButton', 'Chat', 'bottom');
setTippy('chatCleanButton', 'Clean', 'bottom');
setTippy('chatSaveButton', 'Save', 'bottom');
setTippy('chatCloseButton', 'Close', 'bottom');
setTippy('chatMessage', 'Press enter to send', 'top-start');
setTippy('chatSendButton', 'Send', 'top');
setTippy('chatEmojiButton', 'Emoji', 'top');
setTippy('fullScreenButton', 'Full Screen', 'bottom');
setTippy('recButton', 'Recording', 'bottom');
setTippy('startRecButton', 'Start Recording', 'bottom');
setTippy('stopRecButton', 'Stop Recording', 'bottom');
setTippy('pauseRecButton', 'Pause Recording', 'bottom');
setTippy('resumeRecButton', 'Resume Recording', 'bottom');
setTippy('stopAudioButton', 'Stop Audio', 'bottom');
setTippy('startAudioButton', 'Start Audio', 'bottom');
setTippy('swapCameraButton', 'Swap Camera', 'bottom');
setTippy('startVideoButton', 'Start Video', 'bottom');
setTippy('stopVideoButton', 'Stop Video', 'bottom');
setTippy('startScreenButton', 'Start Screen', 'bottom');
setTippy('stopScreenButton', 'Stop Screen', 'bottom');
setTippy('aboutButton', 'About', 'bottom');
}
initEnumerateDevices();
}
function setTippy(elem, content, placement) {
tippy(document.getElementById(elem), {
content: content,
placement: placement,
});
}
// ####################################################
// ENUMERATE DEVICES
// ####################################################
async function initEnumerateDevices() {
if (isEnumerateDevices) return;
console.log('01 ----> init Enumerate Devices');
// allow the audio
await navigator.mediaDevices
.getUserMedia({ audio: true })
.then((stream) => {
enumerateAudioDevices(stream);
isAudioAllowed = true;
})
.catch(() => {
isAudioAllowed = false;
});
// allow the video
await navigator.mediaDevices
.getUserMedia({ video: true })
.then((stream) => {
enumerateVideoDevices(stream);
isVideoAllowed = true;
})
.catch(() => {
isVideoAllowed = false;
});
if (!isAudioAllowed && !isVideoAllowed) {
window.location.href = `/permission?room_id=${room_id}&message=Not allowed both Audio and Video`;
} else {
getPeerInfo();
getPeerGeoLocation();
whoAreYou();
}
}
function enumerateAudioDevices(stream) {
console.log('02 ----> Get Audio Devices');
navigator.mediaDevices
.enumerateDevices()
.then((devices) =>
devices.forEach((device) => {
let el = null;
if ('audioinput' === device.kind) {
el = microphoneSelect;
} else if ('audiooutput' === device.kind) {
el = speakerSelect;
}
if (!el) return;
appenChild(device, el);
}),
)
.then(() => {
stopTracks(stream);
isEnumerateDevices = true;
speakerSelect.disabled = !('sinkId' in HTMLMediaElement.prototype);
});
}
function enumerateVideoDevices(stream) {
console.log('03 ----> Get Video Devices');
navigator.mediaDevices
.enumerateDevices()
.then((devices) =>
devices.forEach((device) => {
let el = null;
if ('videoinput' === device.kind) {
el = videoSelect;
}
if (!el) return;
appenChild(device, el);
}),
)
.then(() => {
stopTracks(stream);
isEnumerateDevices = true;
});
}
function stopTracks(stream) {
stream.getTracks().forEach((track) => {
track.stop();
});
}
function appenChild(device, el) {
let option = document.createElement('option');
option.value = device.deviceId;
option.innerText = device.label;
el.appendChild(option);
}
// ####################################################
// SOME PEER INFO
// ####################################################
function getPeerInfo() {
peer_info = {
detectRTCversion: DetectRTC.version,
isWebRTCSupported: DetectRTC.isWebRTCSupported,
isMobileDevice: DetectRTC.isMobileDevice,
osName: DetectRTC.osName,
osVersion: DetectRTC.osVersion,
browserName: DetectRTC.browser.name,
browserVersion: DetectRTC.browser.version,
};
}
function getPeerGeoLocation() {
fetch(url.ipLookup)
.then((res) => res.json())
.then((outJson) => {
peer_geo = outJson;
})
.catch((ex) => console.warn('IP Lookup', ex));
}
// ####################################################
// ENTER YOUR NAME | Enable/Disable AUDIO/VIDEO
// ####################################################
function whoAreYou() {
console.log('04 ----> Who are you');
Swal.fire({
allowOutsideClick: false,
allowEscapeKey: false,
background: swalBackground,
input: 'text',
inputPlaceholder: 'Enter your name',
html: `<br />
<button id="initAudioButton" class="fas fa-microphone" onclick="handleAudio(event)"></button>
<button id="initVideoButton" class="fas fa-video" onclick="handleVideo(event)"></button>`,
confirmButtonText: `Join meeting`,
showClass: {
popup: 'animate__animated animate__fadeInDown',
},
hideClass: {
popup: 'animate__animated animate__fadeOutUp',
},
inputValidator: (name) => {
if (!name) return 'Please enter your name';
peer_name = name;
},
}).then(() => {
shareRoom();
joinRoom(peer_name, room_id);
});
let initAudioButton = document.getElementById('initAudioButton');
let initVideoButton = document.getElementById('initVideoButton');
if (!isAudioAllowed) initAudioButton.className = 'hidden';
if (!isVideoAllowed) initVideoButton.className = 'hidden';
if (!DetectRTC.isMobileDevice) {
setTippy('initAudioButton', 'Enable / Disable audio', 'left');
setTippy('initVideoButton', 'Enable / Disable video', 'right');
}
}
function handleAudio(e) {
isAudioOn ? (isAudioOn = false) : (isAudioOn = true);
e.target.className = 'fas fa-microphone' + (isAudioOn ? '' : '-slash');
}
function handleVideo(e) {
isVideoOn ? (isVideoOn = false) : (isVideoOn = true);
e.target.className = 'fas fa-video' + (isVideoOn ? '' : '-slash');
}
// ####################################################
// SHARE ROOM
// ####################################################
async function shareRoom(useNavigator = false) {
if (navigator.share && useNavigator) {
try {
await navigator.share({ url: RoomURL });
userLog('info', 'Room Shared successfully', 'top-end');
} catch (err) {
share();
}
} else {
share();
}
function share() {
sound('open');
Swal.fire({
background: swalBackground,
imageUrl: swalImageUrl,
position: 'center',
title: '<strong>Hello ' + peer_name + '</strong>',
html:
`
<br/>
<div id="qrRoomContainer">
<canvas id="qrRoom"></canvas>
</div>
<br/><br/>
<p style="color:white;">Share this meeting invite others to join.</p>
<p style="color:rgb(8, 189, 89);">` +
RoomURL +
`</p>`,
showDenyButton: true,
showCancelButton: true,
confirmButtonText: `Copy meeting URL`,
denyButtonText: `Email invite`,
cancelButtonText: `Close`,
showClass: {
popup: 'animate__animated animate__fadeInUp',
},
hideClass: {
popup: 'animate__animated animate__fadeOutUp',
},
}).then((result) => {
if (result.isConfirmed) {
copyRoomURL();
} else if (result.isDenied) {
let message = {
email: '',
subject: 'Please join our MiroTalkSfu Video Chat Meeting',
body: 'Click to join: ' + RoomURL,
};
shareRoomByEmail(message);
}
});
makeRoomQR();
}
}
// ####################################################
// ROOM UTILITY
// ####################################################
function makeRoomQR() {
let qr = new QRious({
element: document.getElementById('qrRoom'),
value: RoomURL,
});
qr.set({
size: 128,
});
}
function copyRoomURL() {
let tmpInput = document.createElement('input');
document.body.appendChild(tmpInput);
tmpInput.value = RoomURL;
tmpInput.select();
document.execCommand('copy');
document.body.removeChild(tmpInput);
userLog('info', 'Room URL copied to clipboard', 'top-end');
}
function shareRoomByEmail(message) {
let email = message.email;
let subject = message.subject;
let emailBody = message.body;
document.location = 'mailto:' + email + '?subject=' + subject + '&body=' + emailBody;
}
// ####################################################
// JOIN TO ROOM
// ####################################################
function joinRoom(peer_name, room_id) {
if (rc && rc.isConnected()) {
console.log('Already connected to a room');
} else {
console.log('05 ----> join to Room ' + room_id);
rc = new RoomClient(
localMedia,
remoteVideos,
remoteAudios,
window.mediasoupClient,
socket,
room_id,
peer_name,
peer_geo,
peer_info,
isAudioAllowed,
isVideoAllowed,
isAudioOn,
isVideoOn,
roomIsReady,
);
handleRoomClientEvents();
}
}
function roomIsReady() {
control.className = '';
show(exitButton);
show(shareButton);
show(recButton);
show(startRecButton);
show(chatButton);
show(chatSendButton);
show(chatEmojiButton);
if (DetectRTC.isMobileDevice) {
show(swapCameraButton);
setChatSize();
} else {
rc.makeDraggable(chatRoom, chatHeader);
show(startScreenButton);
}
if (DetectRTC.browser.name != 'Safari') {
document.onfullscreenchange = () => {
if (!document.fullscreenElement) rc.isDocumentOnFullScreen = false;
};
show(fullScreenButton);
}
show(devicesButton);
if (isAudioAllowed) show(startAudioButton);
if (isVideoAllowed) show(startVideoButton);
show(videoMedia);
show(aboutButton);
handleButtons();
handleSelects();
handleInputs();
startSessionTimer();
}
function hide(elem) {
elem.className = 'hidden';
}
function show(elem) {
elem.className = '';
}
// ####################################################
// SET CHAT MOBILE
// ####################################################
function setChatSize() {
document.documentElement.style.setProperty('--msger-width', '99%');
document.documentElement.style.setProperty('--msger-height', '99%');
}
// ####################################################
// SESSION TIMER
// ####################################################
function startSessionTimer() {
sessionTime.style.display = 'inline';
let callStartTime = Date.now();
setInterval(function printTime() {
let callElapsedTime = Date.now() - callStartTime;
sessionTime.innerHTML = getTimeToString(callElapsedTime);
}, 1000);
}
function getTimeToString(time) {
let diffInHrs = time / 3600000;
let hh = Math.floor(diffInHrs);
let diffInMin = (diffInHrs - hh) * 60;
let mm = Math.floor(diffInMin);
let diffInSec = (diffInMin - mm) * 60;
let ss = Math.floor(diffInSec);
let formattedHH = hh.toString().padStart(2, '0');
let formattedMM = mm.toString().padStart(2, '0');
let formattedSS = ss.toString().padStart(2, '0');
return `${formattedHH}:${formattedMM}:${formattedSS}`;
}
// ####################################################
// RECORDING TIMER
// ####################################################
function secondsToHms(d) {
d = Number(d);
let h = Math.floor(d / 3600);
let m = Math.floor((d % 3600) / 60);
let s = Math.floor((d % 3600) % 60);
let hDisplay = h > 0 ? h + 'h' : '';
let mDisplay = m > 0 ? m + 'm' : '';
let sDisplay = s > 0 ? s + 's' : '';
return hDisplay + ' ' + mDisplay + ' ' + sDisplay;
}
function startRecordingTimer() {
recElapsedTime = 0;
recTimer = setInterval(function printTime() {
if (rc.isRecording()) {
recElapsedTime++;
recordingStatus.innerHTML = '🔴 REC ' + secondsToHms(recElapsedTime);
}
}, 1000);
}
function stopRecordingTimer() {
clearInterval(recTimer);
}
// ####################################################
// HTML BUTTONS
// ####################################################
function handleButtons() {
exitButton.onclick = () => {
rc.exit();
};
shareButton.onclick = () => {
shareRoom(true);
};
devicesButton.onclick = () => {
rc.toggleDevices();
};
chatButton.onclick = () => {
rc.toggleChat();
};
chatCleanButton.onclick = () => {
rc.chatClean();
};
chatSaveButton.onclick = () => {
rc.chatSave();
};
chatCloseButton.onclick = () => {
rc.toggleChat();
};
chatSendButton.onclick = () => {
rc.sendMessage();
};
chatEmojiButton.onclick = () => {
rc.toggleChatEmoji();
};
fullScreenButton.onclick = () => {
rc.toggleFullScreen();
};
recButton.onclick = () => {
rc.toggleRecording();
};
startRecButton.onclick = () => {
rc.startRecording();
};
stopRecButton.onclick = () => {
rc.stopRecording();
};
pauseRecButton.onclick = () => {
rc.pauseRecording();
};
resumeRecButton.onclick = () => {
rc.resumeRecording();
};
swapCameraButton.onclick = () => {
rc.closeThenProduce(RoomClient.mediaType.video, null, true);
};
startAudioButton.onclick = () => {
rc.produce(RoomClient.mediaType.audio, microphoneSelect.value);
// rc.resumeProducer(RoomClient.mediaType.audio);
};
stopAudioButton.onclick = () => {
rc.closeProducer(RoomClient.mediaType.audio);
// rc.pauseProducer(RoomClient.mediaType.audio);
};
startVideoButton.onclick = () => {
rc.produce(RoomClient.mediaType.video, videoSelect.value);
// rc.resumeProducer(RoomClient.mediaType.video);
};
stopVideoButton.onclick = () => {
rc.closeProducer(RoomClient.mediaType.video);
// rc.pauseProducer(RoomClient.mediaType.video);
};
startScreenButton.onclick = () => {
rc.produce(RoomClient.mediaType.screen);
};
stopScreenButton.onclick = () => {
rc.closeProducer(RoomClient.mediaType.screen);
};
aboutButton.onclick = () => {
showAbout();
};
}
// ####################################################
// HTML SELECTS
// ####################################################
function handleSelects() {
microphoneSelect.onchange = () => {
rc.closeThenProduce(RoomClient.mediaType.audio, microphoneSelect.value);
};
speakerSelect.onchange = () => {
rc.attachSinkId(localMedia, speakerSelect.value);
};
videoSelect.onchange = () => {
rc.closeThenProduce(RoomClient.mediaType.video, videoSelect.value);
};
}
// ####################################################
// HTML INPUTS
// ####################################################
function handleInputs() {
chatMessage.onkeyup = (e) => {
if (e.keyCode === 13) {
e.preventDefault();
chatSendButton.click();
}
};
rc.getId('chatEmoji').addEventListener('emoji-click', (e) => {
chatMessage.value += e.detail.emoji.unicode;
rc.toggleChatEmoji();
});
}
// ####################################################
// ROOM CLIENT EVENT LISTNERS
// ####################################################
function handleRoomClientEvents() {
rc.on(RoomClient.EVENTS.startRec, () => {
hide(startRecButton);
show(stopRecButton);
show(pauseRecButton);
startRecordingTimer();
});
rc.on(RoomClient.EVENTS.pauseRec, () => {
hide(pauseRecButton);
show(resumeRecButton);
});
rc.on(RoomClient.EVENTS.resumeRec, () => {
hide(resumeRecButton);
show(pauseRecButton);
});
rc.on(RoomClient.EVENTS.stopRec, () => {
hide(stopRecButton);
hide(pauseRecButton);
hide(resumeRecButton);
show(startRecButton);
stopRecordingTimer();
});
rc.on(RoomClient.EVENTS.startAudio, () => {
hide(startAudioButton);
show(stopAudioButton);
});
rc.on(RoomClient.EVENTS.pauseAudio, () => {
console.log('Room Client pause audio');
hide(stopAudioButton);
show(startAudioButton);
});
rc.on(RoomClient.EVENTS.resumeAudio, () => {
console.log('Room Client resume audio');
hide(startAudioButton);
show(stopAudioButton);
});
rc.on(RoomClient.EVENTS.stopAudio, () => {
hide(stopAudioButton);
show(startAudioButton);
});
rc.on(RoomClient.EVENTS.startVideo, () => {
hide(startVideoButton);
show(stopVideoButton);
});
rc.on(RoomClient.EVENTS.pauseVideo, () => {
console.log('Room Client pause video');
hide(stopVideoButton);
show(startVideoButton);
});
rc.on(RoomClient.EVENTS.resumeVideo, () => {
console.log('Room Client resume video');
hide(startVideoButton);
show(stopVideoButton);
});
rc.on(RoomClient.EVENTS.stopVideo, () => {
hide(stopVideoButton);
show(startVideoButton);
});
rc.on(RoomClient.EVENTS.startScreen, () => {
hide(startScreenButton);
show(stopScreenButton);
});
rc.on(RoomClient.EVENTS.pauseScreen, () => {
console.log('Room Client pause screen');
});
rc.on(RoomClient.EVENTS.resumeScreen, () => {
console.log('Room Client resume screen');
});
rc.on(RoomClient.EVENTS.stopScreen, () => {
hide(stopScreenButton);
show(startScreenButton);
});
rc.on(RoomClient.EVENTS.exitRoom, () => {
window.location.href = '/newroom';
});
}
// ####################################################
// SHOW LOG
// ####################################################
function userLog(icon, message, position) {
const Toast = Swal.mixin({
background: swalBackground,
toast: true,
position: position,
showConfirmButton: false,
timer: 3000,
});
Toast.fire({
icon: icon,
title: message,
});
}
// ####################################################
// SOUND
// ####################################################
async function sound(name) {
let sound = '../sounds/' + name + '.wav';
let audioToPlay = new Audio(sound);
try {
await audioToPlay.play();
} catch (err) {
return false;
}
}
// ####################################################
// ABOUT
// ####################################################
function showAbout() {
sound('open');
Swal.fire({
background: swalBackground,
imageUrl: swalImageUrl,
position: 'center',
html: `
<br/>
<div id="about">
<b>Open Source</b> project on
<a href="https://github.com/miroslavpejic85/mirotalksfu" target="_blank"><br/><br />
<img alt="mirotalksfu-github" src="../images/github.png"></a><br/><br />
<button class="far fa-heart pulsate" onclick="window.open('https://github.com/sponsors/miroslavpejic85?o=esb')"> Sponsor</button>
<br /><br />
Contact: <a href="https://www.linkedin.com/in/miroslav-pejic-976a07101/" target="_blank"> Miroslav Pejic</a>
</div>
`,
showClass: {
popup: 'animate__animated animate__fadeInUp',
},
hideClass: {
popup: 'animate__animated animate__fadeOutUp',
},
});
}

1369
public/js/RoomClient.js Normal file

تم حذف اختلاف الملف لأن الملف كبير جداً تحميل الاختلاف

58
public/js/landing.js Normal file
عرض الملف

@@ -0,0 +1,58 @@
!(function () {
window;
const e = document.documentElement;
if ((e.classList.remove('no-js'), e.classList.add('js'), document.body.classList.contains('has-animations'))) {
(window.sr = ScrollReveal()).reveal('.feature, .pricing-table-inner', {
duration: 600,
distance: '20px',
easing: 'cubic-bezier(0.5, -0.01, 0, 1.005)',
origin: 'bottom',
interval: 100,
}),
e.classList.add('anime-ready'),
anime
.timeline({ targets: '.hero-figure-box-05' })
.add({
duration: 400,
easing: 'easeInOutExpo',
scaleX: [0.05, 0.05],
scaleY: [0, 1],
perspective: '500px',
delay: anime.random(0, 400),
})
.add({ duration: 400, easing: 'easeInOutExpo', scaleX: 1 })
.add({
duration: 800,
rotateY: '-15deg',
rotateX: '8deg',
rotateZ: '-1deg',
}),
anime
.timeline({ targets: '.hero-figure-box-06, .hero-figure-box-07' })
.add({
duration: 400,
easing: 'easeInOutExpo',
scaleX: [0.05, 0.05],
scaleY: [0, 1],
perspective: '500px',
delay: anime.random(0, 400),
})
.add({ duration: 400, easing: 'easeInOutExpo', scaleX: 1 })
.add({ duration: 800, rotateZ: '20deg' }),
anime({
targets:
'.hero-figure-box-01, .hero-figure-box-02, .hero-figure-box-03, .hero-figure-box-04, .hero-figure-box-08, .hero-figure-box-09, .hero-figure-box-10',
duration: anime.random(600, 800),
delay: anime.random(600, 800),
rotate: [
anime.random(-360, 360),
function (e) {
return e.getAttribute('data-rotation');
},
],
scale: [0.7, 1],
opacity: [0, 1],
easing: 'easeInOutExpo',
});
}
})();

155
public/js/newroom.js Normal file
عرض الملف

@@ -0,0 +1,155 @@
'use strict';
let adjectives = [
'small',
'big',
'large',
'smelly',
'new',
'happy',
'shiny',
'old',
'clean',
'nice',
'bad',
'cool',
'hot',
'cold',
'warm',
'hungry',
'slow',
'fast',
'red',
'white',
'black',
'blue',
'green',
'basic',
'strong',
'cute',
'poor',
'nice',
'huge',
'rare',
'lucky',
'weak',
'tall',
'short',
'tiny',
'great',
'long',
'single',
'rich',
'young',
'dirty',
'fresh',
'brown',
'dark',
'crazy',
'sad',
'loud',
'brave',
'calm',
'silly',
'smart',
];
let nouns = [
'dog',
'bat',
'wrench',
'apple',
'pear',
'ghost',
'cat',
'wolf',
'squid',
'goat',
'snail',
'hat',
'sock',
'plum',
'bear',
'snake',
'turtle',
'horse',
'spoon',
'fork',
'spider',
'tree',
'chair',
'table',
'couch',
'towel',
'panda',
'bread',
'grape',
'cake',
'brick',
'rat',
'mouse',
'bird',
'oven',
'phone',
'photo',
'frog',
'bear',
'camel',
'sheep',
'shark',
'tiger',
'zebra',
'duck',
'eagle',
'fish',
'kitten',
'lobster',
'monkey',
'owl',
'puppy',
'pig',
'rabbit',
'fox',
'whale',
'beaver',
'gorilla',
'lizard',
'parrot',
'sloth',
'swan',
];
function getRandomNumber(length) {
let result = '';
let characters = '0123456789';
let charactersLength = characters.length;
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}
let adjective = adjectives[Math.floor(Math.random() * adjectives.length)];
let noun = nouns[Math.floor(Math.random() * nouns.length)];
let num = getRandomNumber(5);
noun = noun.charAt(0).toUpperCase() + noun.substring(1);
adjective = adjective.charAt(0).toUpperCase() + adjective.substring(1);
document.getElementById('roomName').value = '';
// ####################################################
// TYPPING EFECT
// ####################################################
let i = 0;
let txt = num + adjective + noun;
let speed = 100;
function typeWriter() {
if (i < txt.length) {
document.getElementById('roomName').value += txt.charAt(i);
i++;
setTimeout(typeWriter, speed);
}
}
typeWriter();