[mirotalk] - #281 - Add Support for Custom Avatar via Options Object

This commit is contained in:
Miroslav Pejic
2025-04-08 00:33:25 +02:00
parent b3dc1e2452
commit b22fe1b2ee
16 changed files with 220 additions and 82 deletions
+50 -26
View File
@@ -107,13 +107,14 @@
<br/>
- You can `directly join a room` by using links like:
- https://p2p.mirotalk.com/join?room=test&name=mirotalk&audio=0&video=0&screen=0&hide=0&notify=0
- https://mirotalk.up.railway.app/join?room=test&name=mirotalk&audio=0&video=0&screen=0&hide=0&notify=0
- https://p2p.mirotalk.com/join?room=test&name=mirotalk&avatar=0&audio=0&video=0&screen=0&hide=0&notify=0
- https://mirotalk.up.railway.app/join?room=test&name=mirotalk&avatar=0&audio=0&video=0&screen=0&hide=0&notify=0
| Params | Type | Description |
| ------ | ------- | --------------- |
| room | string | Room Id |
| name | string | User name |
| avatar | Mixed | User avatar |
| audio | boolean | Audio stream |
| video | boolean | Video stream |
| screen | boolean | Screen stream |
@@ -233,33 +234,56 @@ To embed a meeting within `your service or app` using an iframe, you can use the
- `Rest API:` The [API documentation](https://docs.mirotalk.com/mirotalk-p2p/api/) uses [swagger](https://swagger.io/) at http://localhost:3000/api/v1/docs. Or check it out on [live](https://p2p.mirotalk.com/api/v1/docs).
### 1. Stats Endpoint (Get server statistics)
```bash
# The response will give you the total of rooms and peers.
$ curl -X GET "http://localhost:3000/api/v1/stats" -H "authorization: mirotalkp2p_default_secret" -H "Content-Type: application/json"
$ curl -X GET "https://p2p.mirotalk.com/api/v1/stats" -H "authorization: mirotalkp2p_default_secret" -H "Content-Type: application/json"
$ curl -X GET "https://mirotalk.up.railway.app/api/v1/stats" -H "authorization: mirotalkp2p_default_secret" -H "Content-Type: application/json"
# The response will give you the active meetings (default disabled).
$ curl -X GET "http://localhost:3000/api/v1/meetings" -H "authorization: mirotalkp2p_default_secret" -H "Content-Type: application/json"
$ curl -X GET "https://p2p.mirotalk.com/api/v1/meetings" -H "authorization: mirotalkp2p_default_secret" -H "Content-Type: application/json"
$ curl -X GET "mirotalk.up.railway.app/api/v1/meetings" -H "authorization: mirotalkp2p_default_secret" -H "Content-Type: application/json"
# The response will give you a entrypoint / Room URL for your meeting.
$ curl -X POST "http://localhost:3000/api/v1/meeting" -H "authorization: mirotalkp2p_default_secret" -H "Content-Type: application/json"
$ curl -X POST "https://p2p.mirotalk.com/api/v1/meeting" -H "authorization: mirotalkp2p_default_secret" -H "Content-Type: application/json"
$ curl -X POST "https://mirotalk.up.railway.app/api/v1/meeting" -H "authorization: mirotalkp2p_default_secret" -H "Content-Type: application/json"
# The response will give you a entrypoint / URL for the direct join to the meeting.
$ curl -X POST "http://localhost:3000/api/v1/join" -H "authorization: mirotalkp2p_default_secret" -H "Content-Type: application/json" --data '{"room":"test","name":"mirotalk","audio":"true","video":"true","screen":"false","hide":"false","notify":"true"}'
$ curl -X POST "https://p2p.mirotalk.com/api/v1/join" -H "authorization: mirotalkp2p_default_secret" -H "Content-Type: application/json" --data '{"room":"test","name":"mirotalk","audio":"true","video":"true","screen":"false","hide":"false","notify":"true"}'
$ curl -X POST "https://mirotalk.up.railway.app/api/v1/join" -H "authorization: mirotalkp2p_default_secret" -H "Content-Type: application/json" --data '{"room":"test","name":"mirotalk","audio":"true","video":"true","screen":"false","hide":"false","notify":"true"}'
# The response will give you an entry point/URL for direct joining to the meeting with a token.
$ curl -X POST "http://localhost:3000/api/v1/join" -H "authorization: mirotalkp2p_default_secret" -H "Content-Type: application/json" --data '{"room":"test","name":"mirotalk","audio":"true","video":"true","screen":"false","hide":"false","notify":"true","token":{"username":"username","password":"password","presenter":"true", "expire":"1h"}}'
$ curl -X POST "https://p2p.mirotalk.com/api/v1/join" -H "authorization: mirotalkp2p_default_secret" -H "Content-Type: application/json" --data '{"room":"test","name":"mirotalk","audio":"true","video":"true","screen":"false","hide":"false","notify":"true","token":{"username":"username","password":"password","presenter":"true", "expire":"1h"}}'
$ curl -X POST "https://mirotalk.up.railway.app/api/v1/join" -H "authorization: mirotalkp2p_default_secret" -H "Content-Type: application/json" --data '{"room":"test","name":"mirotalk","audio":"true","video":"true","screen":"false","hide":"false","notify":"true","token":{"username":"username","password":"password","presenter":"true", "expire":"1h"}}'
# The response will give you a valid token for a meeting.
$ curl -X POST "http://localhost:3000/api/v1/token" -H "authorization: mirotalkp2p_default_secret" -H "Content-Type: application/json" --data '{"username":"username","password":"password","presenter":"true", "expire":"1h"}'
$ curl -X POST "https://p2p.mirotalk.com/api/v1/token" -H "authorization: mirotalkp2p_default_secret" -H "Content-Type: application/json" --data '{"username":"username","password":"password","presenter":"true", "expire":"1h"}'
$ curl -X POST "https://mirotalk.up.railway.app/api/v1/join" -H "authorization: mirotalkp2p_default_secret" -H "Content-Type: application/json" --data '{"username":"username","password":"password","presenter":"true", "expire":"1h"}'
curl -X GET "http://localhost:3000/api/v1/stats" -H "authorization: mirotalkp2p_default_secret" -H "Content-Type: application/json"
curl -X GET "https://p2p.mirotalk.com/api/v1/stats" -H "authorization: mirotalkp2p_default_secret" -H "Content-Type: application/json"
curl -X GET "https://mirotalk.up.railway.app/api/v1/stats" -H "authorization: mirotalkp2p_default_secret" -H "Content-Type: application/json"
```
### 2. Meetings Endpoint (Get active meetings)
```bash
curl -X GET "http://localhost:3000/api/v1/meetings" -H "authorization: mirotalkp2p_default_secret" -H "Content-Type: application/json"
curl -X GET "https://p2p.mirotalk.com/api/v1/meetings" -H "authorization: mirotalkp2p_default_secret" -H "Content-Type: application/json"
curl -X GET "https://mirotalk.up.railway.app/api/v1/meetings" -H "authorization: mirotalkp2p_default_secret" -H "Content-Type: application/json"
```
### 3. Create Meeting
```bash
curl -X POST "http://localhost:3000/api/v1/meeting" -H "authorization: mirotalkp2p_default_secret" -H "Content-Type: application/json"
curl -X POST "https://p2p.mirotalk.com/api/v1/meeting" -H "authorization: mirotalkp2p_default_secret" -H "Content-Type: application/json"
curl -X POST "https://mirotalk.up.railway.app/api/v1/meeting" -H "authorization: mirotalkp2p_default_secret" -H "Content-Type: application/json"
```
### 4. Join Meeting (Basic)
```bash
curl -X POST "http://localhost:3000/api/v1/join" -H "authorization: mirotalkp2p_default_secret" -H "Content-Type: application/json" --data '{"room":"test","name":"mirotalk","avatar":false,"audio":true,"video":true,"screen":false,"hide":false,"notify":true}'
curl -X POST "https://p2p.mirotalk.com/api/v1/join" -H "authorization: mirotalkp2p_default_secret" -H "Content-Type: application/json" --data '{"room":"test","name":"mirotalk","avatar":false,"audio":true,"video":true,"screen":false,"hide":false,"notify":true}'
curl -X POST "https://mirotalk.up.railway.app/api/v1/join" -H "authorization: mirotalkp2p_default_secret" -H "Content-Type: application/json" --data '{"room":"test","name":"mirotalk","avatar":false,"audio":true,"video":true,"screen":false,"hide":false,"notify":true}'
```
### 5. Join Meeting with Token
```bash
curl -X POST "http://localhost:3000/api/v1/join" -H "authorization: mirotalkp2p_default_secret" -H "Content-Type: application/json" --data '{"room":"test","name":"mirotalk","audio":true,"video":true,"screen":false,"hide":false,"notify":true,"token":{"username":"username","password":"password","presenter":true,"expire":"1h"}}'
curl -X POST "https://p2p.mirotalk.com/api/v1/join" -H "authorization: mirotalkp2p_default_secret" -H "Content-Type: application/json" --data '{"room":"test","name":"mirotalk","audio":true,"video":true,"screen":false,"hide":false,"notify":true,"token":{"username":"username","password":"password","presenter":true,"expire":"1h"}}'
curl -X POST "https://mirotalk.up.railway.app/api/v1/join" -H "authorization: mirotalkp2p_default_secret" -H "Content-Type: application/json" --data '{"room":"test","name":"mirotalk","audio":true,"video":true,"screen":false,"hide":false,"notify":true,"token":{"username":"username","password":"password","presenter":true,"expire":"1h"}}'
```
### 6. Generate Token
```bash
curl -X POST "http://localhost:3000/api/v1/token" -H "authorization: mirotalkp2p_default_secret" -H "Content-Type: application/json" --data '{"username":"username","password":"password","presenter":true,"expire":"1h"}'
curl -X POST "https://p2p.mirotalk.com/api/v1/token" -H "authorization: mirotalkp2p_default_secret" -H "Content-Type: application/json" --data '{"username":"username","password":"password","presenter":true,"expire":"1h"}'
curl -X POST "https://mirotalk.up.railway.app/api/v1/token" -H "authorization: mirotalkp2p_default_secret" -H "Content-Type: application/json" --data '{"username":"username","password":"password","presenter":true,"expire":"1h"}'
```
These commands should now work correctly with the MiroTalk P2P
</details>
<details open>
+3 -2
View File
@@ -21,8 +21,9 @@ async function getJoin() {
body: JSON.stringify({
room: 'test',
name: 'mirotalk',
audio: true,
video: true,
avatar: false,
audio: false,
video: false,
screen: false,
hide: false,
notify: true,
+3 -2
View File
@@ -20,8 +20,9 @@ curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$data = array(
"room" => "test",
"name" => "mirotalk",
"audio" => true,
"video" => true,
"avatar" => false,
"audio" => false,
"video" => false,
"screen" => false,
"hide" => false,
"notify" => true,
+3 -2
View File
@@ -15,8 +15,9 @@ headers = {
data = {
"room": "test",
"name": "mirotalk",
"audio": "true",
"video": "true",
"avatar": "false",
"audio": "false",
"video": "false",
"screen": "false",
"hide": "false",
"notify": "true",
+26 -6
View File
@@ -1,12 +1,32 @@
#!/bin/bash
# Configuration
API_KEY_SECRET="mirotalkp2p_default_secret"
MIROTALK_URL="https://p2p.mirotalk.com/api/v1/join"
# Alternative URLs:
# MIROTALK_URL="http://localhost:3000/api/v1/join"
# MIROTALK_URL = "https://mirotalk.up.railway.app/api/v1/join"
# MIROTALK_URL="https://mirotalk.up.railway.app/api/v1/join"
curl $MIROTALK_URL \
--header "authorization: $API_KEY_SECRET" \
--header "Content-Type: application/json" \
--data '{"room":"test","name":"mirotalk","audio":"true","video":"true","screen":"false","hide":"false","notify":"true","token":{"username":"username","password":"password","presenter":"true", "expire":"1h"}}' \
--request POST
# Join request data
REQUEST_DATA='{
"room": "test",
"name": "mirotalk",
"avatar": false,
"audio": false,
"video": false,
"screen": false,
"hide": false,
"notify": true,
"token": {
"username": "username",
"password": "password",
"presenter": true,
"expire": "1h"
}
}'
# Make the API request
curl -X POST "$MIROTALK_URL" \
-H "authorization: $API_KEY_SECRET" \
-H "Content-Type: application/json" \
-d "$REQUEST_DATA"
+5
View File
@@ -84,6 +84,9 @@ paths:
name:
type: string
default: 'mirotalk'
avatar:
type: string
default: ''
audio:
type: boolean
default: false
@@ -222,6 +225,8 @@ definitions:
properties:
peer_name:
type: string
peer_avatar:
type: string
peer_presenter:
type: boolean
peer_video:
+3 -1
View File
@@ -53,10 +53,11 @@ module.exports = class ServerApi {
getJoinURL(data) {
// Get data
const { room, name, audio, video, screen, notify, hide, token } = data;
const { room, name, avatar, audio, video, screen, notify, hide, token } = data;
const roomValue = room || uuidV4();
const nameValue = name || 'User-' + this.getRandomNumber();
const avatarValue = avatar || false;
const audioValue = audio || false;
const videoValue = video || false;
const screenValue = screen || false;
@@ -70,6 +71,7 @@ module.exports = class ServerApi {
'/join?' +
`room=${roomValue}` +
`&name=${encodeURIComponent(nameValue)}` +
`&avatar=${encodeURIComponent(avatarValue)}` +
`&audio=${audioValue}` +
`&video=${videoValue}` +
`&screen=${screenValue}` +
+10 -4
View File
@@ -45,7 +45,7 @@ dependencies: {
* @license For commercial use or closed source, contact us at license.mirotalk@gmail.com or purchase directly from CodeCanyon
* @license CodeCanyon: https://codecanyon.net/item/mirotalk-p2p-webrtc-realtime-video-conferences/38376661
* @author Miroslav Pejic - miroslav.pejic.85@gmail.com
* @version 1.4.99
* @version 1.5.00
*
*/
@@ -1204,6 +1204,7 @@ io.sockets.on('connect', async (socket) => {
channel_password,
peer_uuid,
peer_name,
peer_avatar,
peer_token,
peer_video,
peer_audio,
@@ -1310,6 +1311,7 @@ io.sockets.on('connect', async (socket) => {
// collect peers info grp by channels
peers[channel][socket.id] = {
peer_name: peer_name,
peer_avatar: peer_avatar,
peer_presenter: isPresenter,
peer_video: peer_video,
peer_audio: peer_audio,
@@ -1458,7 +1460,7 @@ io.sockets.on('connect', async (socket) => {
// Prevent XSS injection
const config = checkXSS(cfg);
// log.debug('Peer name', config);
const { room_id, peer_name_old, peer_name_new } = config;
const { room_id, peer_name_old, peer_name_new, peer_avatar } = config;
let peer_id_to_update = null;
@@ -1481,6 +1483,7 @@ io.sockets.on('connect', async (socket) => {
const data = {
peer_id: peer_id_to_update,
peer_name: peer_name_new,
peer_avatar: peer_avatar,
};
log.debug('[' + socket.id + '] emit peerName to [room_id: ' + room_id + ']', data);
@@ -1556,7 +1559,8 @@ io.sockets.on('connect', async (socket) => {
// Prevent XSS injection
const config = checkXSS(cfg);
// log.debug('Peer action', config);
const { room_id, peer_id, peer_uuid, peer_name, peer_use_video, peer_action, send_to_all } = config;
const { room_id, peer_id, peer_uuid, peer_name, peer_avatar, peer_use_video, peer_action, send_to_all } =
config;
// Only the presenter can do this actions
const presenterActions = ['muteAudio', 'hideVideo', 'ejectAll'];
@@ -1570,6 +1574,7 @@ io.sockets.on('connect', async (socket) => {
const data = {
peer_id: peer_id,
peer_name: peer_name,
peer_avatar: peer_avatar,
peer_action: peer_action,
peer_use_video: peer_use_video,
};
@@ -1622,7 +1627,7 @@ io.sockets.on('connect', async (socket) => {
// Prevent XSS injection
const config = checkXSS(cfg);
// log.debug('File info', config);
const { room_id, peer_id, peer_name, broadcast, file } = config;
const { room_id, peer_id, peer_name, peer_avatar, broadcast, file } = config;
// check if valid fileName
if (!isValidFileName(file.fileName)) {
@@ -1639,6 +1644,7 @@ io.sockets.on('connect', async (socket) => {
log.debug('[' + socket.id + '] Peer [' + peer_name + '] send file to room_id [' + room_id + ']', {
peerName: peer_name,
peerAvatar: peer_avatar,
fileName: file.fileName,
fileSize: bytesToSize(file.fileSize),
fileType: file.fileType,
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "mirotalk",
"version": "1.4.99",
"version": "1.5.00",
"description": "A free WebRTC browser-based video call",
"main": "server.js",
"scripts": {
@@ -61,7 +61,7 @@
"jsonwebtoken": "^9.0.2",
"js-yaml": "^4.1.0",
"nodemailer": "^6.10.0",
"openai": "^4.91.1",
"openai": "^4.92.1",
"qs": "^6.14.0",
"socket.io": "^4.8.1",
"swagger-ui-express": "^5.0.1",
Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

+1 -1
View File
@@ -73,7 +73,7 @@ let brand = {
},
about: {
imageUrl: '../images/mirotalk-logo.gif',
title: 'WebRTC P2P v1.4.99',
title: 'WebRTC P2P v1.5.00',
html: `
<button
id="support-button"
+107 -34
View File
@@ -15,7 +15,7 @@
* @license For commercial use or closed source, contact us at license.mirotalk@gmail.com or purchase directly from CodeCanyon
* @license CodeCanyon: https://codecanyon.net/item/mirotalk-p2p-webrtc-realtime-video-conferences/38376661
* @author Miroslav Pejic - miroslav.pejic.85@gmail.com
* @version 1.4.99
* @version 1.5.00
*
*/
@@ -535,6 +535,7 @@ let isToggleExtraBtnClicked = false;
let myPeerId; // This socket.id
let myPeerUUID = getUUID(); // Unique peer id
let myPeerName = getPeerName();
let myPeerAvatar = getPeerAvatar();
let myToken = getPeerToken(); // peer JWT
let isPresenter = false; // True Who init the room (aka first peer joined)
let myHandStatus = false;
@@ -1037,6 +1038,23 @@ function getPeerName() {
return name;
}
/**
* Check if peer avatar is set
* @returns {string} Peer Avatar
*/
function getPeerAvatar() {
const qs = new URLSearchParams(window.location.search);
const avatar = filterXSS(qs.get('avatar'));
const avatarDisabled = avatar === '0' || avatar === 'false';
console.log('Direct join', { avatar: avatar });
if (avatarDisabled || !isImageURL(avatar)) {
return false;
}
return avatar;
}
/**
* Is screen enabled on join room
* @returns {boolean} true/false
@@ -1940,9 +1958,9 @@ function checkPeerAudioVideo() {
*/
async function whoAreYouJoin() {
myVideoParagraph.innerText = myPeerName + ' (me)';
setPeerAvatarImgName('myVideoAvatarImage', myPeerName);
setPeerAvatarImgName('myProfileAvatar', myPeerName);
setPeerChatAvatarImgName('right', myPeerName);
setPeerAvatarImgName('myVideoAvatarImage', myPeerName, myPeerAvatar);
setPeerAvatarImgName('myProfileAvatar', myPeerName, myPeerAvatar);
setPeerChatAvatarImgName('right', myPeerName, myPeerAvatar);
joinToChannel();
handleHideMe(isHideMeActive);
}
@@ -1959,6 +1977,7 @@ async function joinToChannel() {
peer_info: peerInfo,
peer_uuid: myPeerUUID,
peer_name: myPeerName,
peer_avatar: myPeerAvatar,
peer_token: myToken,
peer_video: useVideo,
peer_audio: useAudio,
@@ -3286,6 +3305,7 @@ async function loadRemoteMediaStream(stream, peers, peer_id, kind) {
console.log('REMOTE PEER INFO', peers[peer_id]);
const peer_name = peers[peer_id]['peer_name'];
const peer_avatar = peers[peer_id]['peer_avatar'];
const peer_audio = peers[peer_id]['peer_audio'];
const peer_video = peers[peer_id]['peer_video'];
const peer_video_status = peers[peer_id]['peer_video_status'];
@@ -3557,7 +3577,7 @@ async function loadRemoteMediaStream(stream, peers, peer_id, kind) {
peer_privacy_status && setVideoPrivacyStatus(remoteMedia.id, peer_privacy_status);
// refresh remote peers avatar name
setPeerAvatarImgName(remoteVideoAvatarImage.id, peer_name);
setPeerAvatarImgName(remoteVideoAvatarImage.id, peer_name, peer_avatar);
// refresh remote peers hand icon status and title
setPeerHandStatus(peer_id, peer_name, peer_hand_status);
// refresh remote peers video icon status and title
@@ -3580,7 +3600,7 @@ async function loadRemoteMediaStream(stream, peers, peer_id, kind) {
toggleClassElements('statusMenu', 'inline');
// notify if peer started to recording own screen + audio
if (peer_rec_status) notifyRecording(peer_id, peer_name, 'Started');
if (peer_rec_status) notifyRecording(peer_id, peer_name, peer_avatar, 'Started');
// Peer without camera, screen sharing OFF
if (!peer_video && !peer_screen_status) {
@@ -3782,15 +3802,24 @@ function genAvatarSvg(peerName, avatarImgSize) {
* Refresh video - chat image avatar on name changes: https://eu.ui-avatars.com/
* @param {string} videoAvatarImageId element id
* @param {string} peerName
* @param {string} peerAvatar
*/
function setPeerAvatarImgName(videoAvatarImageId, peerName) {
function setPeerAvatarImgName(videoAvatarImageId, peerName, peerAvatar) {
const videoAvatarImageElement = getId(videoAvatarImageId);
videoAvatarImageElement.style.pointerEvents = 'none';
if (useAvatarSvg) {
// If a valid avatar image URL is provided
if (peerAvatar && isImageURL(peerAvatar)) {
videoAvatarImageElement.setAttribute('src', peerAvatar);
}
// If not, use SVG based on the email validity
else if (useAvatarSvg) {
const avatarImgSize = isMobileDevice ? 128 : 256;
const avatarImgSvg = isValidEmail(peerName) ? genGravatar(peerName) : genAvatarSvg(peerName, avatarImgSize);
videoAvatarImageElement.setAttribute('src', avatarImgSvg);
} else {
}
// Default fallback avatar
else {
videoAvatarImageElement.setAttribute('src', images.avatar);
}
}
@@ -3799,9 +3828,15 @@ function setPeerAvatarImgName(videoAvatarImageId, peerName) {
* Set Chat avatar image by peer name
* @param {string} avatar position left/right
* @param {string} peerName me or peer name
* @param {string} peerAvatar me or peer avatar
*/
function setPeerChatAvatarImgName(avatar, peerName) {
const avatarImg = isValidEmail(peerName) ? genGravatar(peerName) : genAvatarSvg(peerName, 32);
function setPeerChatAvatarImgName(avatar, peerName, peerAvatar) {
const avatarImg =
peerAvatar && isImageURL(peerAvatar)
? peerAvatar
: isValidEmail(peerName)
? genGravatar(peerName)
: genAvatarSvg(peerName, 32);
switch (avatar) {
case 'left':
@@ -7215,12 +7250,14 @@ function getAudioStreamFromAudioElements() {
* Notify me if someone start to recording they camera/screen/window + audio
* @param {string} fromId peer_id
* @param {string} from peer_name
* @param {string} fromAvatar peer_avatar
* @param {string} action recording action
*/
function notifyRecording(fromId, from, action) {
function notifyRecording(fromId, from, fromAvatar, action) {
const msg = '🔴 ' + action + ' conference recording';
const chatMessage = {
from: from,
fromAvatar: fromAvatar,
fromId: fromId,
to: myPeerName,
msg: msg,
@@ -7829,7 +7866,7 @@ async function sendChatMessage() {
return cleanMessageInput();
}
isChatGPTOn ? await getChatGPTmessage(msg) : emitMsg(myPeerName, 'toAll', msg, false, myPeerId);
isChatGPTOn ? await getChatGPTmessage(msg) : emitMsg(myPeerName, myPeerAvatar, 'toAll', msg, false, myPeerId);
appendMessage(myPeerName, rightChatAvatar, 'right', msg, false);
cleanMessageInput();
}
@@ -7843,6 +7880,7 @@ function handleDataChannelChat(dataMessage) {
// sanitize all params
const msgFrom = filterXSS(dataMessage.from);
const msgFromAvatar = filterXSS(dataMessage.fromAvatar);
const msgFromId = filterXSS(dataMessage.fromId);
const msgTo = filterXSS(dataMessage.to);
const msg = filterXSS(dataMessage.msg);
@@ -7871,7 +7909,7 @@ function handleDataChannelChat(dataMessage) {
userLog('toast', `New message from: ${msgFrom}`);
}
setPeerChatAvatarImgName('left', msgFrom);
setPeerChatAvatarImgName('left', msgFrom, msgFromAvatar);
appendMessage(msgFrom, leftChatAvatar, 'left', msg, msgPrivate, msgId, msgFrom);
speechInMessages ? speechMessage(true, msgFrom, msg) : playSound('chatMessage');
}
@@ -7918,11 +7956,18 @@ function handleSpeechTranscript(config) {
config.text_data = filterXSS(config.text_data);
config.peer_name = filterXSS(config.peer_name);
config.peer_avatar = filterXSS(config.peer_avatar);
const { peer_name, text_data } = config;
const { peer_name, peer_avatar, text_data } = config;
const time_stamp = getFormatDate(new Date());
const avatar_image = isValidEmail(peer_name) ? genGravatar(peer_name) : genAvatarSvg(peer_name, 32);
const avatar_image =
peer_avatar && isImageURL(peer_avatar)
? peer_avatar
: isValidEmail(peer_name)
? genGravatar(peer_name)
: genAvatarSvg(peer_name, 32);
if (!isCaptionBoxVisible) showCaptionDraggable();
@@ -8217,16 +8262,24 @@ async function msgerAddPeers(peers) {
// add all current Participants
for (const peer_id in peers) {
const peer_name = peers[peer_id]['peer_name'];
const peer_avatar = peers[peer_id]['peer_avatar'];
// bypass insert to myself in the list :)
if (peer_id != myPeerId && peer_name) {
const exsistMsgerPrivateDiv = getId(peer_id + '_pMsgDiv');
// if there isn't add it....
if (!exsistMsgerPrivateDiv) {
const avatarSvg = isValidEmail(peer_name) ? genGravatar(peer_name) : genAvatarSvg(peer_name, 24);
//
const chatAvatar =
peer_avatar && isImageURL(peer_avatar)
? peer_avatar
: isValidEmail(peer_name)
? genGravatar(peer_name)
: genAvatarSvg(peer_name, 24);
const msgerPrivateDiv = `
<div id="${peer_id}_pMsgDiv" class="msger-peer-inputarea">
<span style="display: none">${peer_name}</span>
<img id="${peer_id}_pMsgAvatar" class="peer-img" src="${avatarSvg}">
<img id="${peer_id}_pMsgAvatar" class="peer-img" src="${chatAvatar}">
<textarea
rows="1"
cols="1"
@@ -8323,7 +8376,7 @@ function addMsgerPrivateBtn(msgerPrivateBtn, msgerPrivateMsgInput, peerId) {
}
const toPeerName = msgerPrivateBtn.value;
emitMsg(myPeerName, toPeerName, pMsg, true, peerId);
emitMsg(myPeerName, myPeerAvatar, toPeerName, pMsg, true, peerId);
appendMessage(myPeerName, rightChatAvatar, 'right', pMsg, true, null, toPeerName);
msgerPrivateMsgInput.value = '';
elemDisplay(msgerCP, false);
@@ -8404,8 +8457,14 @@ function isValidHttpURL(input) {
* @param {string} url to check
* @returns {boolean} true/false
*/
function isImageURL(url) {
return url.match(/\.(jpeg|jpg|gif|png|tiff|bmp)$/) != null;
async function isImageURL(url) {
try {
const response = await fetch(url, { method: 'HEAD' });
const contentType = response.headers.get('content-type');
return contentType && contentType.startsWith('image/');
} catch {
return false;
}
}
/**
@@ -8515,16 +8574,18 @@ function getFormatDate(date) {
/**
* Send message over Secure dataChannels
* @param {string} from peer name
* @param {string} fromAvatar peer avatar
* @param {string} to peer name
* @param {string} msg message to send
* @param {boolean} privateMsg if is a private message
* @param {string} id peer_id
*/
function emitMsg(from, to, msg, privateMsg, id) {
function emitMsg(from, fromAvatar, to, msg, privateMsg, id) {
if (!msg) return;
// sanitize all params
const getFrom = filterXSS(from);
const getFromAvatar = filterXSS(fromAvatar);
const getFromId = filterXSS(myPeerId);
const getTo = filterXSS(to);
const getMsg = filterXSS(msg);
@@ -8534,6 +8595,7 @@ function emitMsg(from, to, msg, privateMsg, id) {
const chatMessage = {
type: 'chat',
from: getFrom,
fromAvatar: getFromAvatar,
fromId: getFromId,
id: getId,
to: getTo,
@@ -8704,6 +8766,7 @@ async function updateMyPeerName() {
room_id: roomId,
peer_name_old: myOldPeerName,
peer_name_new: myPeerName,
peer_avatar: myPeerAvatar,
});
myPeerNameSet.value = '';
@@ -8711,9 +8774,9 @@ async function updateMyPeerName() {
window.localStorage.peer_name = myPeerName;
setPeerAvatarImgName('myVideoAvatarImage', myPeerName);
setPeerAvatarImgName('myProfileAvatar', myPeerName);
setPeerChatAvatarImgName('right', myPeerName);
setPeerAvatarImgName('myVideoAvatarImage', myPeerName, myPeerAvatar);
setPeerAvatarImgName('myProfileAvatar', myPeerName, myPeerAvatar);
setPeerChatAvatarImgName('right', myPeerName, myPeerAvatar);
userLog('toast', 'My name changed to ' + myPeerName);
}
@@ -8722,18 +8785,25 @@ async function updateMyPeerName() {
* @param {object} config data
*/
function handlePeerName(config) {
const { peer_id, peer_name } = config;
const { peer_id, peer_name, peer_avatar } = config;
const videoName = getId(peer_id + '_name');
if (videoName) videoName.innerText = peer_name;
// change also avatar and btn value - name on chat lists....
const msgerPeerName = getId(peer_id + '_pMsgBtn');
const msgerPeerAvatar = getId(peer_id + '_pMsgAvatar');
if (msgerPeerName) msgerPeerName.value = peer_name;
if (msgerPeerAvatar) {
msgerPeerAvatar.src = isValidEmail(peer_name) ? genGravatar(peer_name) : genAvatarSvg(peer_name, 32);
msgerPeerAvatar.src =
peer_avatar && isImageURL(peer_avatar)
? peer_avatar
: isValidEmail(peer_name)
? genGravatar(peer_name)
: genAvatarSvg(peer_name, 32);
}
// refresh also peer video avatar name
setPeerAvatarImgName(peer_id + '_avatar', peer_name);
setPeerAvatarImgName(peer_id + '_avatar', peer_name, peer_avatar);
}
/**
@@ -8992,7 +9062,7 @@ function sendPrivateMsgToPeer(toPeerId, toPeerName) {
isChatPasteTxt = false;
return;
}
emitMsg(myPeerName, toPeerName, pMsg, true, toPeerId);
emitMsg(myPeerName, myPeerAvatar, toPeerName, pMsg, true, toPeerId);
appendMessage(myPeerName, rightChatAvatar, 'right', pMsg, true, null, toPeerName);
userLog('toast', 'Message sent to ' + toPeerName + ' 👍');
}
@@ -9059,6 +9129,7 @@ async function emitPeersAction(peerAction) {
sendToServer('peerAction', {
room_id: roomId,
peer_name: myPeerName,
peer_avatar: myPeerAvatar,
peer_id: myPeerId,
peer_uuid: myPeerUUID,
peer_use_video: useVideo,
@@ -9078,6 +9149,7 @@ async function emitPeerAction(peer_id, peerAction) {
sendToServer('peerAction', {
room_id: roomId,
peer_id: peer_id,
peer_avatar: myPeerAvatar,
peer_use_video: useVideo,
peer_name: myPeerName,
peer_action: peerAction,
@@ -9091,7 +9163,7 @@ async function emitPeerAction(peer_id, peerAction) {
*/
function handlePeerAction(config) {
console.log('Handle peer action: ', config);
const { peer_id, peer_name, peer_use_video, peer_action } = config;
const { peer_id, peer_name, peer_avatar, peer_use_video, peer_action } = config;
switch (peer_action) {
case 'muteAudio':
@@ -9101,10 +9173,10 @@ function handlePeerAction(config) {
setMyVideoOff(peer_name);
break;
case 'recStart':
notifyRecording(peer_id, peer_name, 'Start');
notifyRecording(peer_id, peer_name, peer_avatar, 'Start');
break;
case 'recStop':
notifyRecording(peer_id, peer_name, 'Stop');
notifyRecording(peer_id, peer_name, peer_avatar, 'Stop');
break;
case 'screenStart':
handleScreenStart(peer_id);
@@ -10574,6 +10646,7 @@ function sendFileInformations(file, peer_id, broadcast = false) {
room_id: roomId,
broadcast: broadcast,
peer_name: myPeerName,
peer_avatar: myPeerAvatar,
peer_id: peer_id,
file: {
fileName: fileToSend.name,
@@ -10640,7 +10713,7 @@ function handleFileInfo(config) {
incomingFileInfo.file.fileType;
console.log(fileToReceiveInfo);
// generate chat avatar by peer_name
setPeerChatAvatarImgName('left', incomingFileInfo.peer_name);
setPeerChatAvatarImgName('left', incomingFileInfo.peer_name, incomingFileInfo.peer_avatar);
// keep track of received file on chat
appendMessage(
incomingFileInfo.peer_name,
@@ -11085,7 +11158,7 @@ function showAbout() {
Swal.fire({
background: swBg,
position: 'center',
title: brand.about?.title && brand.about.title.trim() !== '' ? brand.about.title : 'WebRTC P2P v1.4.99',
title: brand.about?.title && brand.about.title.trim() !== '' ? brand.about.title : 'WebRTC P2P v1.5.00',
imageUrl: brand.about?.imageUrl && brand.about.imageUrl.trim() !== '' ? brand.about.imageUrl : images.about,
customClass: { image: 'img-about' },
html: `
+2
View File
@@ -2,6 +2,7 @@ class IframeApi {
static DEFAULT_OPTIONS = {
room: 'default-room',
name: 'guest',
avatar: false,
audio: false,
video: false,
screen: false,
@@ -44,6 +45,7 @@ class IframeApi {
const params = new URLSearchParams({
room: this.options.room,
name: this.options.name,
avatar: this.options.avatar ? this.options.avatar : 0,
audio: this.options.audio ? 1 : 0,
video: this.options.video ? 1 : 0,
screen: this.options.screen ? 1 : 0,
+1
View File
@@ -112,6 +112,7 @@ if (speechRecognition) {
type: 'speech',
room_id: roomId,
peer_name: myPeerName,
peer_avatar: myPeerAvatar,
text_data: transcript,
time_stamp: new Date(),
};
+1
View File
@@ -14,6 +14,7 @@
const options = {
room: 'test',
name: 'mirotalk',
avatar: 'https://p2p.mirotalk.com/images/mirotalk-logo.png',
audio: 0,
video: 0,
screen: 0,
+3 -2
View File
@@ -136,6 +136,7 @@ describe('test-api', () => {
const data = {
room: 'room1',
name: 'John Doe',
avatar: 'avatar.jpg',
audio: true,
video: false,
screen: false,
@@ -148,7 +149,7 @@ describe('test-api', () => {
const result = serverApi.getJoinURL(data);
result.should.equal(
'https://example.com/join?room=room1&name=John%20Doe&audio=true&video=false&screen=false&hide=false&notify=false&token=testToken',
'https://example.com/join?room=room1&name=John%20Doe&avatar=avatar.jpg&audio=true&video=false&screen=false&hide=false&notify=false&token=testToken',
);
tokenStub.restore();
@@ -166,7 +167,7 @@ describe('test-api', () => {
const result = serverApi.getJoinURL({});
result.should.equal(
'https://example.com/join?room=room1&name=User-123456&audio=false&video=false&screen=false&hide=false&notify=false',
'https://example.com/join?room=room1&name=User-123456&avatar=false&audio=false&video=false&screen=false&hide=false&notify=false',
);
});
});