[mirotalksfu] - improve restAPI and docs
هذا الالتزام موجود في:
@@ -17,18 +17,22 @@ Some examples demonstrating how to call the API:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# js
|
# js
|
||||||
|
node meetings.js
|
||||||
node meeting.js
|
node meeting.js
|
||||||
node join.js
|
node join.js
|
||||||
|
|
||||||
# php
|
# php
|
||||||
|
php meetings.php
|
||||||
php meeting.php
|
php meeting.php
|
||||||
php join.php
|
php join.php
|
||||||
|
|
||||||
# python
|
# python
|
||||||
|
python3 meetings.py
|
||||||
python3 meeting.py
|
python3 meeting.py
|
||||||
python3 join.py
|
python3 join.py
|
||||||
|
|
||||||
# bash
|
# bash
|
||||||
|
./meetings.sh
|
||||||
./meeting.sh
|
./meeting.sh
|
||||||
./join.sh
|
./join.sh
|
||||||
```
|
```
|
||||||
|
|||||||
34
app/api/meetings/meetings.js
Normal file
34
app/api/meetings/meetings.js
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
async function getMeetings() {
|
||||||
|
try {
|
||||||
|
// Use dynamic import with await
|
||||||
|
const { default: fetch } = await import('node-fetch');
|
||||||
|
|
||||||
|
const API_KEY_SECRET = 'mirotalksfu_default_secret';
|
||||||
|
const MIROTALK_URL = 'https://sfu.mirotalk.com/api/v1/meetings';
|
||||||
|
//const MIROTALK_URL = 'http://localhost:3010/api/v1/meetings';
|
||||||
|
|
||||||
|
const response = await fetch(MIROTALK_URL, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
authorization: API_KEY_SECRET,
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const data = await response.json();
|
||||||
|
if (data.error) {
|
||||||
|
console.log('Error:', data.error);
|
||||||
|
} else {
|
||||||
|
if (data && data.meetings) {
|
||||||
|
const meetings = data.meetings;
|
||||||
|
const formattedData = JSON.stringify({ meetings }, null, 2);
|
||||||
|
console.log(formattedData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching data:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getMeetings();
|
||||||
29
app/api/meetings/meetings.php
Normal file
29
app/api/meetings/meetings.php
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
$API_KEY_SECRET = "mirotalksfu_default_secret";
|
||||||
|
$MIROTALK_URL = "https://sfu.mirotalk.com/api/v1/meetings";
|
||||||
|
//$MIROTALK_URL = "http://localhost:3010/api/v1/meetings";
|
||||||
|
|
||||||
|
$ch = curl_init();
|
||||||
|
curl_setopt($ch, CURLOPT_URL, $MIROTALK_URL);
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPGET, true);
|
||||||
|
|
||||||
|
$headers = [
|
||||||
|
'authorization:' . $API_KEY_SECRET,
|
||||||
|
'Content-Type: application/json'
|
||||||
|
];
|
||||||
|
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
echo "Status code: $httpcode \n";
|
||||||
|
|
||||||
|
if ($response) {
|
||||||
|
echo json_encode(json_decode($response), JSON_PRETTY_PRINT);
|
||||||
|
} else {
|
||||||
|
echo "Failed to retrieve data.\n";
|
||||||
|
}
|
||||||
26
app/api/meetings/meetings.py
Normal file
26
app/api/meetings/meetings.py
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# pip3 install requests
|
||||||
|
import requests
|
||||||
|
import json
|
||||||
|
|
||||||
|
API_KEY_SECRET = "mirotalksfu_default_secret"
|
||||||
|
MIROTALK_URL = "https://sfu.mirotalk.com/api/v1/meetings"
|
||||||
|
#MIROTALK_URL = "http://localhost:3010/api/v1/meetings"
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
"authorization": API_KEY_SECRET,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.get(
|
||||||
|
MIROTALK_URL,
|
||||||
|
headers=headers
|
||||||
|
)
|
||||||
|
|
||||||
|
print("Status code:", response.status_code)
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
data = response.json()
|
||||||
|
pretty_printed_data = json.dumps(data, indent=4)
|
||||||
|
print(data)
|
||||||
|
else:
|
||||||
|
print("Failed to retrieve data. Error:", response.text)
|
||||||
10
app/api/meetings/meetings.sh
Executable file
10
app/api/meetings/meetings.sh
Executable file
@@ -0,0 +1,10 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
API_KEY_SECRET="mirotalksfu_default_secret"
|
||||||
|
MIROTALK_URL="https://sfu.mirotalk.com/api/v1/meetings"
|
||||||
|
#MIROTALK_URL="http://localhost:3010/api/v1/meetings"
|
||||||
|
|
||||||
|
curl $MIROTALK_URL \
|
||||||
|
--header "authorization: $API_KEY_SECRET" \
|
||||||
|
--header "Content-Type: application/json" \
|
||||||
|
--request GET
|
||||||
@@ -2,8 +2,8 @@ swagger: '2.0'
|
|||||||
|
|
||||||
info:
|
info:
|
||||||
title: MiroTalk SFU API
|
title: MiroTalk SFU API
|
||||||
description: API description for external applications that integrates with MiroTalk SFU.
|
description: API description for external applications that integrate with MiroTalk SFU.
|
||||||
version: 1.0.0
|
version: 1.0.1
|
||||||
|
|
||||||
basePath: /api/v1
|
basePath: /api/v1
|
||||||
|
|
||||||
@@ -12,6 +12,23 @@ schemes:
|
|||||||
- http
|
- http
|
||||||
|
|
||||||
paths:
|
paths:
|
||||||
|
/meetings:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- 'meetings'
|
||||||
|
summary: 'Get meetings'
|
||||||
|
description: 'Get meetings'
|
||||||
|
produces:
|
||||||
|
- 'application/json'
|
||||||
|
security:
|
||||||
|
- secretApiKey: []
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: 'Get Meetings done'
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/MeetingsResponse'
|
||||||
|
'403':
|
||||||
|
description: 'Unauthorized!'
|
||||||
/meeting:
|
/meeting:
|
||||||
post:
|
post:
|
||||||
tags:
|
tags:
|
||||||
@@ -42,50 +59,7 @@ paths:
|
|||||||
name: Join
|
name: Join
|
||||||
description: Custom Join URL.
|
description: Custom Join URL.
|
||||||
schema:
|
schema:
|
||||||
type: object
|
$ref: '#/definitions/JoinRequest'
|
||||||
properties:
|
|
||||||
room:
|
|
||||||
type: string
|
|
||||||
default: 'test'
|
|
||||||
roomPassword:
|
|
||||||
type: ['boolean', 'string'] # Allow boolean or string type
|
|
||||||
default: false
|
|
||||||
name:
|
|
||||||
type: string
|
|
||||||
default: 'mirotalksfu'
|
|
||||||
audio:
|
|
||||||
type: boolean
|
|
||||||
default: false
|
|
||||||
video:
|
|
||||||
type: boolean
|
|
||||||
default: false
|
|
||||||
screen:
|
|
||||||
type: boolean
|
|
||||||
default: false
|
|
||||||
hide:
|
|
||||||
type: boolean
|
|
||||||
default: false
|
|
||||||
notify:
|
|
||||||
type: boolean
|
|
||||||
default: false
|
|
||||||
token:
|
|
||||||
type: object
|
|
||||||
description: |
|
|
||||||
Object containing authentication token details.
|
|
||||||
This token is required when host.protected or host.user_auth is enabled in the app/src/config.js file.
|
|
||||||
properties:
|
|
||||||
username:
|
|
||||||
type: string
|
|
||||||
default: 'username'
|
|
||||||
password:
|
|
||||||
type: string
|
|
||||||
default: 'password'
|
|
||||||
presenter:
|
|
||||||
type: boolean
|
|
||||||
default: true
|
|
||||||
expire:
|
|
||||||
type: string
|
|
||||||
default: '1h'
|
|
||||||
consumes:
|
consumes:
|
||||||
- 'application/json'
|
- 'application/json'
|
||||||
produces:
|
produces:
|
||||||
@@ -108,13 +82,93 @@ securityDefinitions:
|
|||||||
description: 'Format like this: authorization: {API_KEY_SECRET}'
|
description: 'Format like this: authorization: {API_KEY_SECRET}'
|
||||||
|
|
||||||
definitions:
|
definitions:
|
||||||
|
MeetingsResponse:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
meetings:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/Meeting'
|
||||||
MeetingResponse:
|
MeetingResponse:
|
||||||
type: 'object'
|
type: 'object'
|
||||||
properties:
|
properties:
|
||||||
meeting:
|
meeting:
|
||||||
type: 'string'
|
type: string
|
||||||
|
JoinRequest:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
room:
|
||||||
|
type: string
|
||||||
|
default: 'test'
|
||||||
|
roomPassword:
|
||||||
|
type: ['boolean', 'string'] # Allow boolean or string type
|
||||||
|
default: false
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
default: 'mirotalksfu'
|
||||||
|
audio:
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
video:
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
screen:
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
hide:
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
notify:
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
token:
|
||||||
|
$ref: '#/definitions/TokenRequest'
|
||||||
|
TokenRequest:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
username:
|
||||||
|
type: string
|
||||||
|
default: 'username'
|
||||||
|
password:
|
||||||
|
type: string
|
||||||
|
default: 'password'
|
||||||
|
presenter:
|
||||||
|
type: boolean
|
||||||
|
default: true
|
||||||
|
expire:
|
||||||
|
type: string
|
||||||
|
default: '1h'
|
||||||
JoinResponse:
|
JoinResponse:
|
||||||
type: 'object'
|
type: 'object'
|
||||||
properties:
|
properties:
|
||||||
join:
|
join:
|
||||||
type: 'string'
|
type: string
|
||||||
|
Peer:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
presenter:
|
||||||
|
type: boolean
|
||||||
|
video:
|
||||||
|
type: boolean
|
||||||
|
audio:
|
||||||
|
type: boolean
|
||||||
|
screen:
|
||||||
|
type: boolean
|
||||||
|
hand:
|
||||||
|
type: boolean
|
||||||
|
os:
|
||||||
|
type: string
|
||||||
|
browser:
|
||||||
|
type: string
|
||||||
|
|
||||||
|
Meeting:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
roomId:
|
||||||
|
type: string
|
||||||
|
peers:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/Peer'
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ 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.3.68
|
* @version 1.3.69
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -106,8 +106,11 @@ const hostCfg = {
|
|||||||
authenticated: !config.host.protected,
|
authenticated: !config.host.protected,
|
||||||
};
|
};
|
||||||
|
|
||||||
const apiBasePath = '/api/v1'; // api endpoint path
|
const restApi = {
|
||||||
const api_docs = host + apiBasePath + '/docs'; // api docs
|
basePath: '/api/v1', // api endpoint path
|
||||||
|
docs: host + '/api/v1/docs', // api docs
|
||||||
|
allowed: config.api?.allowed,
|
||||||
|
};
|
||||||
|
|
||||||
// Sentry monitoring
|
// Sentry monitoring
|
||||||
const sentryEnabled = config.sentry.enabled;
|
const sentryEnabled = config.sentry.enabled;
|
||||||
@@ -220,7 +223,7 @@ function startServer() {
|
|||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
app.use(express.static(dir.public));
|
app.use(express.static(dir.public));
|
||||||
app.use(bodyParser.urlencoded({ extended: true }));
|
app.use(bodyParser.urlencoded({ extended: true }));
|
||||||
app.use(apiBasePath + '/docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument)); // api docs
|
app.use(restApi.basePath + '/docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument)); // api docs
|
||||||
|
|
||||||
// IP Whitelist check ...
|
// IP Whitelist check ...
|
||||||
app.use(restrictAccessByIP);
|
app.use(restrictAccessByIP);
|
||||||
@@ -483,15 +486,69 @@ function startServer() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// ####################################################
|
// ####################################################
|
||||||
// API
|
// REST API
|
||||||
// ####################################################
|
// ####################################################
|
||||||
|
|
||||||
// request meeting room endpoint
|
// request meetings list
|
||||||
app.post(['/api/v1/meeting'], (req, res) => {
|
app.get([restApi.basePath + '/meetings'], (req, res) => {
|
||||||
|
// Check if endpoint allowed
|
||||||
|
if (restApi.allowed && !restApi.allowed.meetings) {
|
||||||
|
return res
|
||||||
|
.status(403)
|
||||||
|
.json({
|
||||||
|
error: 'This endpoint has been disabled. Please contact the administrator for further information.',
|
||||||
|
});
|
||||||
|
}
|
||||||
// check if user was authorized for the api call
|
// check if user was authorized for the api call
|
||||||
let host = req.headers.host;
|
const { host, authorization } = req.headers;
|
||||||
let authorization = req.headers.authorization;
|
const api = new ServerApi(host, authorization);
|
||||||
let api = new ServerApi(host, authorization);
|
if (!api.isAuthorized()) {
|
||||||
|
log.debug('MiroTalk get meetings - Unauthorized', {
|
||||||
|
header: req.headers,
|
||||||
|
body: req.body,
|
||||||
|
});
|
||||||
|
return res.status(403).json({ error: 'Unauthorized!' });
|
||||||
|
}
|
||||||
|
const meetings = Array.from(roomList.entries()).map(([id, room]) => {
|
||||||
|
const peers = Array.from(room.peers.values()).map((peer) => ({
|
||||||
|
name: peer.peer_info.peer_name,
|
||||||
|
presenter: peer.peer_info.peer_presenter,
|
||||||
|
video: peer.peer_info.peer_video,
|
||||||
|
audio: peer.peer_info.peer_audio,
|
||||||
|
screen: peer.peer_info.peer_screen,
|
||||||
|
hand: peer.peer_info.peer_hand,
|
||||||
|
os: peer.peer_info.os_name ? `${peer.peer_info.os_name} ${peer.peer_info.os_version}` : '',
|
||||||
|
browser: peer.peer_info.browser_name
|
||||||
|
? `${peer.peer_info.browser_name} ${peer.peer_info.browser_version}`
|
||||||
|
: '',
|
||||||
|
}));
|
||||||
|
return {
|
||||||
|
roomId: id,
|
||||||
|
peers: peers,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
res.json({ meetings: meetings });
|
||||||
|
// log.debug the output if all done
|
||||||
|
log.debug('MiroTalk get meetings - Authorized', {
|
||||||
|
header: req.headers,
|
||||||
|
body: req.body,
|
||||||
|
meetings: meetings,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// request meeting room endpoint
|
||||||
|
app.post([restApi.basePath + '/meeting'], (req, res) => {
|
||||||
|
// Check if endpoint allowed
|
||||||
|
if (restApi.allowed && !restApi.allowed.meeting) {
|
||||||
|
return res
|
||||||
|
.status(403)
|
||||||
|
.json({
|
||||||
|
error: 'This endpoint has been disabled. Please contact the administrator for further information.',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// check if user was authorized for the api call
|
||||||
|
const { host, authorization } = req.headers;
|
||||||
|
const api = new ServerApi(host, authorization);
|
||||||
if (!api.isAuthorized()) {
|
if (!api.isAuthorized()) {
|
||||||
log.debug('MiroTalk get meeting - Unauthorized', {
|
log.debug('MiroTalk get meeting - Unauthorized', {
|
||||||
header: req.headers,
|
header: req.headers,
|
||||||
@@ -501,8 +558,7 @@ function startServer() {
|
|||||||
}
|
}
|
||||||
// setup meeting URL
|
// setup meeting URL
|
||||||
let meetingURL = api.getMeetingURL();
|
let meetingURL = api.getMeetingURL();
|
||||||
res.setHeader('Content-Type', 'application/json');
|
res.json({ meeting: meetingURL });
|
||||||
res.end(JSON.stringify({ meeting: meetingURL }));
|
|
||||||
// log.debug the output if all done
|
// log.debug the output if all done
|
||||||
log.debug('MiroTalk get meeting - Authorized', {
|
log.debug('MiroTalk get meeting - Authorized', {
|
||||||
header: req.headers,
|
header: req.headers,
|
||||||
@@ -512,11 +568,18 @@ function startServer() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// request join room endpoint
|
// request join room endpoint
|
||||||
app.post(['/api/v1/join'], (req, res) => {
|
app.post([restApi.basePath + '/join'], (req, res) => {
|
||||||
|
// Check if endpoint allowed
|
||||||
|
if (restApi.allowed && !restApi.allowed.join) {
|
||||||
|
return res
|
||||||
|
.status(403)
|
||||||
|
.json({
|
||||||
|
error: 'This endpoint has been disabled. Please contact the administrator for further information.',
|
||||||
|
});
|
||||||
|
}
|
||||||
// check if user was authorized for the api call
|
// check if user was authorized for the api call
|
||||||
let host = req.headers.host;
|
const { host, authorization } = req.headers;
|
||||||
let authorization = req.headers.authorization;
|
const api = new ServerApi(host, authorization);
|
||||||
let api = new ServerApi(host, authorization);
|
|
||||||
if (!api.isAuthorized()) {
|
if (!api.isAuthorized()) {
|
||||||
log.debug('MiroTalk get join - Unauthorized', {
|
log.debug('MiroTalk get join - Unauthorized', {
|
||||||
header: req.headers,
|
header: req.headers,
|
||||||
@@ -526,8 +589,7 @@ function startServer() {
|
|||||||
}
|
}
|
||||||
// setup Join URL
|
// setup Join URL
|
||||||
let joinURL = api.getJoinURL(req.body);
|
let joinURL = api.getJoinURL(req.body);
|
||||||
res.setHeader('Content-Type', 'application/json');
|
res.json({ join: joinURL });
|
||||||
res.end(JSON.stringify({ join: joinURL }));
|
|
||||||
// log.debug the output if all done
|
// log.debug the output if all done
|
||||||
log.debug('MiroTalk get join - Authorized', {
|
log.debug('MiroTalk get join - Authorized', {
|
||||||
header: req.headers,
|
header: req.headers,
|
||||||
@@ -593,7 +655,7 @@ function startServer() {
|
|||||||
announcedAddress: announcedAddress,
|
announcedAddress: announcedAddress,
|
||||||
server: host,
|
server: host,
|
||||||
server_tunnel: tunnel,
|
server_tunnel: tunnel,
|
||||||
api_docs: api_docs,
|
rest_api: restApi,
|
||||||
mediasoup_worker_bin: mediasoup.workerBin,
|
mediasoup_worker_bin: mediasoup.workerBin,
|
||||||
mediasoup_server_version: mediasoup.version,
|
mediasoup_server_version: mediasoup.version,
|
||||||
mediasoup_client_version: mediasoupClient.version,
|
mediasoup_client_version: mediasoupClient.version,
|
||||||
@@ -644,7 +706,7 @@ function startServer() {
|
|||||||
middleware: config.middleware,
|
middleware: config.middleware,
|
||||||
announcedAddress: announcedAddress,
|
announcedAddress: announcedAddress,
|
||||||
server: host,
|
server: host,
|
||||||
api_docs: api_docs,
|
rest_api: restApi,
|
||||||
mediasoup_worker_bin: mediasoup.workerBin,
|
mediasoup_worker_bin: mediasoup.workerBin,
|
||||||
mediasoup_server_version: mediasoup.version,
|
mediasoup_server_version: mediasoup.version,
|
||||||
mediasoup_client_version: mediasoupClient.version,
|
mediasoup_client_version: mediasoupClient.version,
|
||||||
|
|||||||
@@ -196,8 +196,15 @@ module.exports = {
|
|||||||
authToken: '',
|
authToken: '',
|
||||||
},
|
},
|
||||||
api: {
|
api: {
|
||||||
// app/api
|
// Default secret key for app/api
|
||||||
keySecret: 'mirotalksfu_default_secret',
|
keySecret: 'mirotalksfu_default_secret',
|
||||||
|
// Define which endpoints are allowed
|
||||||
|
allowed: {
|
||||||
|
meetings: false,
|
||||||
|
meeting: true,
|
||||||
|
join: true,
|
||||||
|
//...
|
||||||
|
},
|
||||||
},
|
},
|
||||||
sentry: {
|
sentry: {
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "mirotalksfu",
|
"name": "mirotalksfu",
|
||||||
"version": "1.3.68",
|
"version": "1.3.69",
|
||||||
"description": "WebRTC SFU browser-based video calls",
|
"description": "WebRTC SFU browser-based video calls",
|
||||||
"main": "Server.js",
|
"main": "Server.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -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.3.68
|
* @version 1.3.69
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -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.3.68
|
* @version 1.3.69
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -3689,12 +3689,6 @@ class RoomClient {
|
|||||||
<span class="text-start " id="${chatMessagesId}">${getMsg}</span>
|
<span class="text-start " id="${chatMessagesId}">${getMsg}</span>
|
||||||
<hr/>
|
<hr/>
|
||||||
<div class="about-buttons mt5">
|
<div class="about-buttons mt5">
|
||||||
<button
|
|
||||||
id="msg-delete-${chatMessagesId}"
|
|
||||||
class="mr5"
|
|
||||||
onclick="rc.deleteMessage('msg-${chatMessagesId}')">
|
|
||||||
<i class="fas fa-trash"></i>
|
|
||||||
</button>
|
|
||||||
<button
|
<button
|
||||||
id="msg-copy-${chatMessagesId}"
|
id="msg-copy-${chatMessagesId}"
|
||||||
class="mr5"
|
class="mr5"
|
||||||
@@ -3702,6 +3696,12 @@ class RoomClient {
|
|||||||
<i class="fas fa-paste"></i>
|
<i class="fas fa-paste"></i>
|
||||||
</button>
|
</button>
|
||||||
${speechButton}
|
${speechButton}
|
||||||
|
<button
|
||||||
|
id="msg-delete-${chatMessagesId}"
|
||||||
|
class="mr5"
|
||||||
|
onclick="rc.deleteMessage('msg-${chatMessagesId}')">
|
||||||
|
<i class="fas fa-trash"></i>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
المرجع في مشكلة جديدة
حظر مستخدم