From b5d4a4239ff676cf3501fe64c84dcf91b4cfb60c Mon Sep 17 00:00:00 2001 From: Miroslav Pejic Date: Fri, 3 Jan 2025 10:06:51 +0100 Subject: [PATCH] [mirotalksfu] - add room duration --- app/api/join/join.js | 1 + app/api/join/join.php | 3 +- app/api/join/join.py | 1 + app/api/join/join.sh | 2 +- app/api/swagger.yaml | 3 ++ app/src/Server.js | 9 ++-- app/src/ServerApi.js | 4 +- package.json | 2 +- public/js/Room.js | 97 +++++++++++++++++++++++++++++++---------- public/js/RoomClient.js | 2 +- tests/test-ServerAPI.js | 5 ++- 11 files changed, 95 insertions(+), 34 deletions(-) diff --git a/app/api/join/join.js b/app/api/join/join.js index 2136daf8..0a6efe9d 100644 --- a/app/api/join/join.js +++ b/app/api/join/join.js @@ -24,6 +24,7 @@ async function getJoin() { screen: true, hide: false, notify: true, + duration: 'unlimited', token: { username: 'username', password: 'password', diff --git a/app/api/join/join.php b/app/api/join/join.php index 1deabeb2..eafb0626 100644 --- a/app/api/join/join.php +++ b/app/api/join/join.php @@ -2,7 +2,7 @@ $API_KEY_SECRET = "mirotalksfu_default_secret"; $MIROTALK_URL = "https://sfu.mirotalk.com/api/v1/join"; -// $MIROTALK_URL = "http://localhost:3010/api/v1/join"; +//$MIROTALK_URL = "http://localhost:3010/api/v1/join"; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $MIROTALK_URL); @@ -25,6 +25,7 @@ "screen" => true, "hide" => false, "notify" => true, + "duration" => "unlimited", "token" => array( "username" => "username", "password" => "password", diff --git a/app/api/join/join.py b/app/api/join/join.py index effd3eb6..2e737087 100644 --- a/app/api/join/join.py +++ b/app/api/join/join.py @@ -20,6 +20,7 @@ "screen": "true", "hide": "false", "notify": "true", + "duration": "unlimited", "token": { "username": "username", "password": "password", diff --git a/app/api/join/join.sh b/app/api/join/join.sh index e834c1a6..df765457 100755 --- a/app/api/join/join.sh +++ b/app/api/join/join.sh @@ -7,5 +7,5 @@ MIROTALK_URL="https://sfu.mirotalk.com/api/v1/join" curl $MIROTALK_URL \ --header "authorization: $API_KEY_SECRET" \ --header "Content-Type: application/json" \ - --data '{"room":"test","roomPassword":"false","name":"mirotalksfu","audio":"true","video":"true","screen":"false","hide":"false","notify":"true","token":{"username":"username","password":"password","presenter":"true", "expire":"1h"}}' \ + --data '{"room":"test","roomPassword":"false","name":"mirotalksfu","audio":"true","video":"true","screen":"false","hide":"false","notify":"true","duration":"unlimited","token":{"username":"username","password":"password","presenter":"true", "expire":"1h"}}' \ --request POST \ No newline at end of file diff --git a/app/api/swagger.yaml b/app/api/swagger.yaml index 5de53eb2..a823ce24 100644 --- a/app/api/swagger.yaml +++ b/app/api/swagger.yaml @@ -177,6 +177,9 @@ definitions: notify: type: boolean default: false + duration: + type: string + default: 'unlimited' token: $ref: '#/definitions/TokenRequest' TokenRequest: diff --git a/app/src/Server.js b/app/src/Server.js index c563bce0..4fb605a6 100644 --- a/app/src/Server.js +++ b/app/src/Server.js @@ -55,7 +55,7 @@ dev dependencies: { * @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 * @author Miroslav Pejic - miroslav.pejic.85@gmail.com - * @version 1.6.74 + * @version 1.6.75 * */ @@ -513,12 +513,11 @@ function startServer() { log.debug('Direct Join', req.query); - // http://localhost:3010/join?room=test&roomPassword=0&name=mirotalksfu&audio=1&video=1&screen=0&hide=0¬ify=1 + // http://localhost:3010/join?room=test&roomPassword=0&name=mirotalksfu&audio=1&video=1&screen=0&hide=0¬ify=1&duration=00:00:20 // http://localhost:3010/join?room=test&roomPassword=0&name=mirotalksfu&audio=1&video=1&screen=0&hide=0¬ify=0&token=token - const { room, roomPassword, name, audio, video, screen, hide, notify, token, isPresenter } = checkXSS( - req.query, - ); + const { room, roomPassword, name, audio, video, screen, hide, notify, duration, token, isPresenter } = + checkXSS(req.query); if (!room) { log.warn('/join/params room empty', room); diff --git a/app/src/ServerApi.js b/app/src/ServerApi.js index 693d7922..18e74c4d 100644 --- a/app/src/ServerApi.js +++ b/app/src/ServerApi.js @@ -68,7 +68,7 @@ module.exports = class ServerApi { getJoinURL(data) { // Get data - const { room, roomPassword, name, audio, video, screen, hide, notify, token } = data; + const { room, roomPassword, name, audio, video, screen, hide, notify, duration, token } = data; const roomValue = room || uuidV4(); const nameValue = name || 'User-' + this.getRandomNumber(); @@ -78,6 +78,7 @@ module.exports = class ServerApi { const screenValue = screen || false; const hideValue = hide || false; const notifyValue = notify || false; + const durationValue = duration || 'unlimited'; const jwtToken = token ? '&token=' + this.getToken(token) : ''; const joinURL = @@ -92,6 +93,7 @@ module.exports = class ServerApi { `&screen=${screenValue}` + `&hide=${hideValue}` + `¬ify=${notifyValue}` + + `&duration=${durationValue}` + jwtToken; return joinURL; diff --git a/package.json b/package.json index 98ba3ae8..28b37426 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mirotalksfu", - "version": "1.6.74", + "version": "1.6.75", "description": "WebRTC SFU browser-based video calls", "main": "Server.js", "scripts": { diff --git a/public/js/Room.js b/public/js/Room.js index 44bac61a..1b6de47c 100644 --- a/public/js/Room.js +++ b/public/js/Room.js @@ -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 CodeCanyon: https://codecanyon.net/item/mirotalk-sfu-webrtc-realtime-video-conferences/40769970 * @author Miroslav Pejic - miroslav.pejic.85@gmail.com - * @version 1.6.74 + * @version 1.6.75 * */ @@ -191,6 +191,7 @@ let chatMessagesId = 0; let room_id = getRoomId(); let room_password = getRoomPassword(); +let room_duration = getRoomDuration(); let peer_name = getPeerName(); let peer_uuid = getPeerUUID(); let peer_token = getPeerToken(); @@ -440,13 +441,21 @@ function setTippy(elem, content, placement, allowHTML = false) { } } +// #################################################### +// HELPERS +// #################################################### + +function getQueryParam(param) { + const urlParams = new URLSearchParams(window.location.search); + return filterXSS(urlParams.get(param)); +} + // #################################################### // GET ROOM ID // #################################################### function getRoomId() { - let qs = new URLSearchParams(window.location.search); - let queryRoomId = filterXSS(qs.get('room')); + let queryRoomId = getQueryParam('room'); let roomId = queryRoomId ? queryRoomId : location.pathname.substring(6); if (roomId == '') { roomId = makeId(12); @@ -732,12 +741,11 @@ function hasVideoTrack(mediaStream) { } // #################################################### -// API CHECK +// QUERY PARAMS CHECK // #################################################### function getScreen() { - let qs = new URLSearchParams(window.location.search); - let screen = filterXSS(qs.get('screen')); + let screen = getQueryParam('screen'); if (screen) { screen = screen.toLowerCase(); let queryScreen = screen === '1' || screen === 'true'; @@ -751,8 +759,7 @@ function getScreen() { } function getNotify() { - let qs = new URLSearchParams(window.location.search); - let notify = filterXSS(qs.get('notify')); + let notify = getQueryParam('notify'); if (notify) { notify = notify.toLowerCase(); let queryNotify = notify === '1' || notify === 'true'; @@ -767,8 +774,7 @@ function getNotify() { } function getHideMeActive() { - let qs = new URLSearchParams(window.location.search); - let hide = filterXSS(qs.get('hide')); + let hide = getQueryParam('hide'); let queryHideMe = false; if (hide) { hide = hide.toLowerCase(); @@ -779,8 +785,7 @@ function getHideMeActive() { } function isPeerPresenter() { - let qs = new URLSearchParams(window.location.search); - let presenter = filterXSS(qs.get('isPresenter')); + let presenter = getQueryParam('isPresenter'); if (presenter) { presenter = presenter.toLowerCase(); let queryPresenter = presenter === '1' || presenter === 'true'; @@ -794,8 +799,7 @@ function isPeerPresenter() { } function getPeerName() { - const qs = new URLSearchParams(window.location.search); - const name = filterXSS(qs.get('name')); + const name = getQueryParam('name'); if (isHtml(name)) { console.log('Direct join', { name: 'Invalid name' }); return 'Invalid name'; @@ -815,8 +819,7 @@ function getPeerUUID() { function getPeerToken() { if (window.sessionStorage.peer_token) return window.sessionStorage.peer_token; - let qs = new URLSearchParams(window.location.search); - let token = filterXSS(qs.get('token')); + let token = getQueryParam('token'); let queryToken = false; if (token) { queryToken = token; @@ -826,8 +829,7 @@ function getPeerToken() { } function getRoomPassword() { - let qs = new URLSearchParams(window.location.search); - let roomPassword = filterXSS(qs.get('roomPassword')); + let roomPassword = getQueryParam('roomPassword'); if (roomPassword) { let queryNoRoomPassword = roomPassword === '0' || roomPassword === 'false'; if (queryNoRoomPassword) { @@ -839,6 +841,58 @@ function getRoomPassword() { return false; } +function getRoomDuration() { + const roomDuration = getQueryParam('duration'); + + if (isValidDuration(roomDuration)) { + if (roomDuration === 'unlimited') { + console.log('The room has no time limit'); + return roomDuration; + } + const timeLimit = timeToMilliseconds(roomDuration); + setTimeout(() => { + Swal.fire({ + title: 'Time Limit Reached', + text: 'The room has reached its time limit and will close shortly', + icon: 'warning', + timer: 6000, // 6 seconds + timerProgressBar: true, + showConfirmButton: false, + allowOutsideClick: false, + didOpen: () => { + Swal.showLoading(); + }, + willClose: () => { + openURL('/'); + }, + }); + }, timeLimit); + + console.log('Direct join', { duration: roomDuration, timeLimit: timeLimit }); + return roomDuration; + } +} + +function timeToMilliseconds(timeString) { + const [hours, minutes, seconds] = timeString.split(':').map(Number); + return (hours * 3600 + minutes * 60 + seconds) * 1000; +} + +function isValidDuration(duration) { + if (duration === 'unlimited') return true; + + // Check if the format is HH:MM:SS + const regex = /^(\d{2}):(\d{2}):(\d{2})$/; + const match = duration.match(regex); + if (!match) return false; + const [hours, minutes, seconds] = match.slice(1).map(Number); + // Validate ranges: hours, minutes, and seconds + if (hours < 0 || minutes < 0 || minutes > 59 || seconds < 0 || seconds > 59) { + return false; + } + return true; +} + // #################################################### // INIT CONFIG // #################################################### @@ -1155,9 +1209,8 @@ function initVideoContainerShow(show = true) { } function checkMedia() { - let qs = new URLSearchParams(window.location.search); - let audio = filterXSS(qs.get('audio')); - let video = filterXSS(qs.get('video')); + let audio = getQueryParam('audio'); + let video = getQueryParam('video'); if (audio) { audio = audio.toLowerCase(); let queryPeerAudio = audio === '1' || audio === 'true'; @@ -4619,7 +4672,7 @@ function showAbout() { imageUrl: image.about, customClass: { image: 'img-about' }, position: 'center', - title: 'WebRTC SFU v1.6.74', + title: 'WebRTC SFU v1.6.75', html: `
diff --git a/public/js/RoomClient.js b/public/js/RoomClient.js index 4543a0a7..4eb961b0 100644 --- a/public/js/RoomClient.js +++ b/public/js/RoomClient.js @@ -9,7 +9,7 @@ * @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 * @author Miroslav Pejic - miroslav.pejic.85@gmail.com - * @version 1.6.74 + * @version 1.6.75 * */ diff --git a/tests/test-ServerAPI.js b/tests/test-ServerAPI.js index a97bd95a..dd886cec 100644 --- a/tests/test-ServerAPI.js +++ b/tests/test-ServerAPI.js @@ -177,6 +177,7 @@ describe('test-ServerAPI', () => { screen: false, hide: false, notify: false, + duration: '00:30:00', token: { username: 'user', password: 'pass', presenter: true, expire: '1h' }, }; @@ -184,7 +185,7 @@ describe('test-ServerAPI', () => { const result = serverApi.getJoinURL(data); result.should.equal( - 'https://example.com/join?room=room1&roomPassword=password123&name=John%20Doe&audio=true&video=false&screen=false&hide=false¬ify=false&token=testToken', + 'https://example.com/join?room=room1&roomPassword=password123&name=John%20Doe&audio=true&video=false&screen=false&hide=false¬ify=false&duration=00:30:00&token=testToken', ); tokenStub.restore(); @@ -202,7 +203,7 @@ describe('test-ServerAPI', () => { const result = serverApi.getJoinURL({}); result.should.equal( - 'https://example.com/join?room=room1&roomPassword=false&name=User-123456&audio=false&video=false&screen=false&hide=false¬ify=false', + 'https://example.com/join?room=room1&roomPassword=false&name=User-123456&audio=false&video=false&screen=false&hide=false¬ify=false&duration=unlimited', ); }); });