From 6e1aa2259a6e4da18d57f5038d5b18270512e803 Mon Sep 17 00:00:00 2001 From: lastpeony Date: Thu, 28 Nov 2024 18:42:48 +0300 Subject: [PATCH 1/6] allow circle settings to be configured through customSettings --- .../streamapp/AMSBroadcastManager.java | 35 +++++++++++++++++-- .../streamapp/ConferenceRoomSettings.java | 22 +++++------- .../WebSocketApplicationHandler.java | 6 ++-- 3 files changed, 42 insertions(+), 21 deletions(-) diff --git a/webapp/src/main/java/io/antmedia/enterprise/streamapp/AMSBroadcastManager.java b/webapp/src/main/java/io/antmedia/enterprise/streamapp/AMSBroadcastManager.java index 03481d3f4..08f224ef4 100644 --- a/webapp/src/main/java/io/antmedia/enterprise/streamapp/AMSBroadcastManager.java +++ b/webapp/src/main/java/io/antmedia/enterprise/streamapp/AMSBroadcastManager.java @@ -1,11 +1,14 @@ package io.antmedia.enterprise.streamapp; +import com.google.gson.Gson; import io.antmedia.AntMediaApplicationAdapter; +import io.antmedia.AppSettings; import io.antmedia.datastore.db.DataStore; import io.antmedia.datastore.db.DataStoreFactory; import io.antmedia.datastore.db.IDataStoreFactory; import io.antmedia.datastore.db.types.Broadcast; import io.antmedia.datastore.db.types.BroadcastUpdate; +import io.antmedia.muxer.IAntMediaStreamHandler; import io.antmedia.rest.model.Result; import io.antmedia.rest.RestServiceBase; import org.jetbrains.annotations.NotNull; @@ -21,14 +24,35 @@ public class AMSBroadcastManager implements ApplicationContextAware { private ApplicationContext applicationContext; private AntMediaApplicationAdapter appAdaptor; + private ConferenceRoomSettings conferenceRoomSettings; + private AppSettings appSettings; + private Gson gson = new Gson(); @Override public void setApplicationContext(@NotNull ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; - } - public ApplicationContext getApplicationContext() { - return applicationContext; + IAntMediaStreamHandler app = getApplication(); + appSettings = app.getAppSettings(); + + Object circleSettingsString = appSettings.getCustomSetting("circle"); + if (circleSettingsString == null) { + logger.error("Using default settings for Conference Room Settings because no Circle settings in the AppSettings"); + + conferenceRoomSettings = new ConferenceRoomSettings(); + } + else { + try { + conferenceRoomSettings = gson.fromJson(circleSettingsString.toString(), ConferenceRoomSettings.class); + } + catch (Exception e) + { + logger.error("Invalid Conference room settings, using default conference room settings"); + conferenceRoomSettings = new ConferenceRoomSettings(); + } + } + conferenceRoomSettings.init(); + } public AntMediaApplicationAdapter getApplication() { @@ -78,4 +102,9 @@ public boolean updateBroadcastRole(String streamId, String role) { return result; } + + public ConferenceRoomSettings getConferenceRoomSettings() { + return conferenceRoomSettings; + } + } diff --git a/webapp/src/main/java/io/antmedia/enterprise/streamapp/ConferenceRoomSettings.java b/webapp/src/main/java/io/antmedia/enterprise/streamapp/ConferenceRoomSettings.java index e96bc5c42..1cfe0f4ed 100644 --- a/webapp/src/main/java/io/antmedia/enterprise/streamapp/ConferenceRoomSettings.java +++ b/webapp/src/main/java/io/antmedia/enterprise/streamapp/ConferenceRoomSettings.java @@ -1,41 +1,34 @@ package io.antmedia.enterprise.streamapp; +import com.google.gson.annotations.Expose; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.PropertySource; - -import com.google.gson.annotations.Expose; import jakarta.annotation.PostConstruct; -@PropertySource("/WEB-INF/red5-web.properties") + public class ConferenceRoomSettings { protected static Logger logger = LoggerFactory.getLogger(ConferenceRoomSettings.class); - - @Value("${roomCreationPassword:#{null}}") private String roomCreationPassword; - @Expose + @Expose private boolean roomCreationPasswordEnabled = false; - - @Expose + + @Expose private boolean isRecordingFeatureAvailable = false; - @Expose - private String participantVisibilityMatrix; + @Expose + private String participantVisibilityMatrix; @Expose private int maxVideoTrackCount = 6; @PostConstruct public void init() { - roomCreationPasswordEnabled = StringUtils.isNotBlank(roomCreationPassword); - logger.info("roomCreationPasswordEnabled is {}", StringUtils.isNotBlank(roomCreationPassword)); try { Class.forName("io.antmedia.plugin.IMediaPushPlugin"); @@ -46,6 +39,7 @@ public void init() { isRecordingFeatureAvailable = false; } + roomCreationPasswordEnabled = StringUtils.isNotBlank(roomCreationPassword); } diff --git a/webapp/src/main/java/io/antmedia/enterprise/streamapp/WebSocketApplicationHandler.java b/webapp/src/main/java/io/antmedia/enterprise/streamapp/WebSocketApplicationHandler.java index 52647405f..dd92fe81b 100644 --- a/webapp/src/main/java/io/antmedia/enterprise/streamapp/WebSocketApplicationHandler.java +++ b/webapp/src/main/java/io/antmedia/enterprise/streamapp/WebSocketApplicationHandler.java @@ -122,9 +122,7 @@ private AMSBroadcastManager getAMSBroadcastManager() { } private void setConferenceRoomSettings(){ - if(context != null){ - conferenceRoomSettings = (ConferenceRoomSettings) context.getBean("conferenceRoomSettings"); - } + conferenceRoomSettings = getAMSBroadcastManager().getConferenceRoomSettings(); } private void setAppSettings() { @@ -326,7 +324,7 @@ private void responseRoomSettings(Session session) { String participantVisibilityMatrix = appSettings.getParticipantVisibilityMatrix().toString(); - if (participantVisibilityMatrix != null) { + if (participantVisibilityMatrix != null && conferenceRoomSettings.getParticipantVisibilityMatrix() == null) { conferenceRoomSettings.setParticipantVisibilityMatrix(participantVisibilityMatrix); } From c806eb4b0046b0e13504cac5fd74222fd035a20c Mon Sep 17 00:00:00 2001 From: Mustafa BOLEKEN Date: Thu, 2 Jan 2025 13:57:04 +0300 Subject: [PATCH 2/6] Update settings on the fly --- .../streamapp/AMSBroadcastManager.java | 50 ++++++++++++------- .../WebSocketApplicationHandler.java | 12 +---- 2 files changed, 32 insertions(+), 30 deletions(-) diff --git a/webapp/src/main/java/io/antmedia/enterprise/streamapp/AMSBroadcastManager.java b/webapp/src/main/java/io/antmedia/enterprise/streamapp/AMSBroadcastManager.java index 08f224ef4..44a292682 100644 --- a/webapp/src/main/java/io/antmedia/enterprise/streamapp/AMSBroadcastManager.java +++ b/webapp/src/main/java/io/antmedia/enterprise/streamapp/AMSBroadcastManager.java @@ -34,25 +34,7 @@ public void setApplicationContext(@NotNull ApplicationContext applicationContext IAntMediaStreamHandler app = getApplication(); appSettings = app.getAppSettings(); - - Object circleSettingsString = appSettings.getCustomSetting("circle"); - if (circleSettingsString == null) { - logger.error("Using default settings for Conference Room Settings because no Circle settings in the AppSettings"); - - conferenceRoomSettings = new ConferenceRoomSettings(); - } - else { - try { - conferenceRoomSettings = gson.fromJson(circleSettingsString.toString(), ConferenceRoomSettings.class); - } - catch (Exception e) - { - logger.error("Invalid Conference room settings, using default conference room settings"); - conferenceRoomSettings = new ConferenceRoomSettings(); - } - } - conferenceRoomSettings.init(); - + fetchConferenceRoomSettings(); } public AntMediaApplicationAdapter getApplication() { @@ -103,6 +85,36 @@ public boolean updateBroadcastRole(String streamId, String role) { return result; } + public void fetchConferenceRoomSettings() { + Object circleSettingsString = appSettings.getCustomSetting("circle"); + if (circleSettingsString == null) { + logger.error("Using default settings for Conference Room Settings because no Circle settings in the AppSettings"); + + conferenceRoomSettings = new ConferenceRoomSettings(); + } + else { + try { + conferenceRoomSettings = gson.fromJson(circleSettingsString.toString(), ConferenceRoomSettings.class); + } + catch (Exception e) + { + logger.error("Invalid Conference room settings, using default conference room settings"); + conferenceRoomSettings = new ConferenceRoomSettings(); + } + } + conferenceRoomSettings.init(); + + String participantVisibilityMatrix = appSettings.getParticipantVisibilityMatrix().toString(); + + if (participantVisibilityMatrix != null && conferenceRoomSettings.getParticipantVisibilityMatrix() == null) { + conferenceRoomSettings.setParticipantVisibilityMatrix(participantVisibilityMatrix); + } + + int maxVideoTrackCount = appSettings.getMaxVideoTrackCount(); + + conferenceRoomSettings.setMaxVideoTrackCount(maxVideoTrackCount); + } + public ConferenceRoomSettings getConferenceRoomSettings() { return conferenceRoomSettings; } diff --git a/webapp/src/main/java/io/antmedia/enterprise/streamapp/WebSocketApplicationHandler.java b/webapp/src/main/java/io/antmedia/enterprise/streamapp/WebSocketApplicationHandler.java index dd92fe81b..6e5501825 100644 --- a/webapp/src/main/java/io/antmedia/enterprise/streamapp/WebSocketApplicationHandler.java +++ b/webapp/src/main/java/io/antmedia/enterprise/streamapp/WebSocketApplicationHandler.java @@ -122,6 +122,7 @@ private AMSBroadcastManager getAMSBroadcastManager() { } private void setConferenceRoomSettings(){ + getAMSBroadcastManager().fetchConferenceRoomSettings(); conferenceRoomSettings = getAMSBroadcastManager().getConferenceRoomSettings(); } @@ -321,17 +322,6 @@ private Object getMediaPushPlugin() { } private void responseRoomSettings(Session session) { - - String participantVisibilityMatrix = appSettings.getParticipantVisibilityMatrix().toString(); - - if (participantVisibilityMatrix != null && conferenceRoomSettings.getParticipantVisibilityMatrix() == null) { - conferenceRoomSettings.setParticipantVisibilityMatrix(participantVisibilityMatrix); - } - - int maxVideoTrackCount = appSettings.getMaxVideoTrackCount(); - - conferenceRoomSettings.setMaxVideoTrackCount(maxVideoTrackCount); - JSONObject jsonResponse = new JSONObject(); jsonResponse.put(WebSocketConstants.COMMAND, WebSocketApplicationConstants.SET_SETTINGS_COMMAND); jsonResponse.put(WebSocketApplicationConstants.SETTINGS, gsonOnlyExposedFields.toJson(conferenceRoomSettings)); From 66868a60c3503e2b092469ebcfeb9ec48a920d02 Mon Sep 17 00:00:00 2001 From: Mustafa BOLEKEN Date: Thu, 2 Jan 2025 15:20:10 +0300 Subject: [PATCH 3/6] Save settings as state --- react/src/pages/AntMedia.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/react/src/pages/AntMedia.js b/react/src/pages/AntMedia.js index c94470168..a57ea6d1b 100644 --- a/react/src/pages/AntMedia.js +++ b/react/src/pages/AntMedia.js @@ -336,6 +336,8 @@ function AntMedia(props) { const [microphoneButtonDisabled, setMicrophoneButtonDisabled] = React.useState(false); const [cameraButtonDisabled, setCameraButtonDisabled] = React.useState(false); + const [settings, setSettings] = React.useState(); + const [screenSharingInProgress, setScreenSharingInProgress] = React.useState(false); const [requestSpeakerList, setRequestSpeakerList] = React.useState([]); @@ -2986,6 +2988,7 @@ function AntMedia(props) { console.log("--maxVideoTrackCountFromAppSettings: ", localSettings?.maxVideoTrackCount); setAppSettingsMaxVideoTrackCount(localSettings?.maxVideoTrackCount > 0 ? localSettings?.maxVideoTrackCount+1 : 6); } + setSettings(localSettings); } else if (obj.command === "startRecordingResponse") { console.log("Incoming startRecordingResponse:", obj); definition = JSON.parse(obj.definition); @@ -3189,7 +3192,8 @@ function AntMedia(props) { processUpdatedStatsForPlaySpeedTest, speedTestCounter, setRoomName, - setPublishStreamId + setPublishStreamId, + settings }} > {props.children} From 0bca5b22b9a86a34e0c9e1b604451c6a2900a17c Mon Sep 17 00:00:00 2001 From: Mustafa BOLEKEN Date: Fri, 3 Jan 2025 10:57:10 +0300 Subject: [PATCH 4/6] Add test codes --- .../BecomePublisherConfirmationDialog.test.js | 27 ++++++++++++++ .../__tests__/Components/DrawerButton.test.js | 32 +++++++++++++++++ .../Components => }/EffectsDrawer.test.js | 4 +-- .../Components/MessageDrawer.test.js | 35 +++++++++++++++++++ 4 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 react/src/__tests__/Components/BecomePublisherConfirmationDialog.test.js create mode 100644 react/src/__tests__/Components/DrawerButton.test.js rename react/src/__tests__/Components/{Footer/Components => }/EffectsDrawer.test.js (90%) create mode 100644 react/src/__tests__/Components/MessageDrawer.test.js diff --git a/react/src/__tests__/Components/BecomePublisherConfirmationDialog.test.js b/react/src/__tests__/Components/BecomePublisherConfirmationDialog.test.js new file mode 100644 index 000000000..69ce0a0d8 --- /dev/null +++ b/react/src/__tests__/Components/BecomePublisherConfirmationDialog.test.js @@ -0,0 +1,27 @@ +// src/BecomePublisherConfirmationDialog.test.js +import React from 'react'; +import { render } from '@testing-library/react'; +import EffectsDrawer from "../../Components/EffectsDrawer"; +import BecomePublisherConfirmationDialog from "../../Components/BecomePublisherConfirmationDialog"; + +// Mock the props +const props = { +} + +describe('Become Publisher Confirmation Dialog Component', () => { + + beforeEach(() => { + // Reset the mock implementation before each test + jest.clearAllMocks(); + }); + + + it('renders without crashing', () => { + render( + + ); + }); + +}); diff --git a/react/src/__tests__/Components/DrawerButton.test.js b/react/src/__tests__/Components/DrawerButton.test.js new file mode 100644 index 000000000..733f54b72 --- /dev/null +++ b/react/src/__tests__/Components/DrawerButton.test.js @@ -0,0 +1,32 @@ +// src/DrawerButton.test.js +import React from 'react'; +import { render } from '@testing-library/react'; +import EffectsDrawer from "../../Components/EffectsDrawer"; +import BecomePublisherConfirmationDialog from "../../Components/BecomePublisherConfirmationDialog"; +import DrawerButton from "../../Components/DrawerButton"; + +// Mock the props +const props = { + handleMessageDrawerOpen: jest.fn(), + handleParticipantListOpen: jest.fn(), + handleEffectsOpen: jest.fn(), + setPublisherRequestListDrawerOpen: jest.fn(), +} + +describe('Drawer Button Component', () => { + + beforeEach(() => { + // Reset the mock implementation before each test + jest.clearAllMocks(); + }); + + + it('renders without crashing', () => { + render( + + ); + }); + +}); diff --git a/react/src/__tests__/Components/Footer/Components/EffectsDrawer.test.js b/react/src/__tests__/Components/EffectsDrawer.test.js similarity index 90% rename from react/src/__tests__/Components/Footer/Components/EffectsDrawer.test.js rename to react/src/__tests__/Components/EffectsDrawer.test.js index 98c11319f..cfdcbbc62 100644 --- a/react/src/__tests__/Components/Footer/Components/EffectsDrawer.test.js +++ b/react/src/__tests__/Components/EffectsDrawer.test.js @@ -1,7 +1,7 @@ // src/EffectsDrawer.test.js import React from 'react'; import { render } from '@testing-library/react'; -import EffectsDrawer from "../../../../Components/EffectsDrawer"; +import EffectsDrawer from "../../Components/EffectsDrawer"; // Mock the props const props = { @@ -28,7 +28,7 @@ jest.mock('react', () => ({ ...jest.requireActual('react'), })); -describe('Camera Button Component', () => { +describe('Effects Drawer Component', () => { beforeEach(() => { // Reset the mock implementation before each test diff --git a/react/src/__tests__/Components/MessageDrawer.test.js b/react/src/__tests__/Components/MessageDrawer.test.js new file mode 100644 index 000000000..e548c6ade --- /dev/null +++ b/react/src/__tests__/Components/MessageDrawer.test.js @@ -0,0 +1,35 @@ +// src/MessageDrawer.test.js +import React from 'react'; +import { render } from '@testing-library/react'; +import EffectsDrawer from "../../Components/EffectsDrawer"; +import BecomePublisherConfirmationDialog from "../../Components/BecomePublisherConfirmationDialog"; +import MessageDrawer from "../../Components/MessageDrawer"; + +// Mock the props +const props = { + handleMessageDrawerOpen: jest.fn(), + handleParticipantListOpen: jest.fn(), + handleEffectsOpen: jest.fn(), + setPublisherRequestListDrawerOpen: jest.fn(), + messages: [], + sendMessage: jest.fn(), + handleSetMessages: jest.fn(), +} + +describe('Message Drawer Component', () => { + + beforeEach(() => { + // Reset the mock implementation before each test + jest.clearAllMocks(); + }); + + + it('renders without crashing', () => { + render( + + ); + }); + +}); From af4c2b0440a9b7b698cbeae04ae2d7a085f82624 Mon Sep 17 00:00:00 2001 From: Mustafa BOLEKEN Date: Sun, 5 Jan 2025 14:13:36 +0300 Subject: [PATCH 5/6] Add integration test code for create meeting with password --- .../Components/RoomCreationPasswordDialog.js | 1 + test/test_join_leave.py | 77 +++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/react/src/Components/Footer/Components/RoomCreationPasswordDialog.js b/react/src/Components/Footer/Components/RoomCreationPasswordDialog.js index 35d48d6dc..ff2857f74 100644 --- a/react/src/Components/Footer/Components/RoomCreationPasswordDialog.js +++ b/react/src/Components/Footer/Components/RoomCreationPasswordDialog.js @@ -65,6 +65,7 @@ export function RoomCreationPasswordDialog(props) { placeholder="Enter room name" /> */} Date: Wed, 8 Jan 2025 12:26:27 +0300 Subject: [PATCH 6/6] Enable the test_create_meeting_with_password --- test/test_join_leave.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_join_leave.py b/test/test_join_leave.py index c98e6fb45..b9ee43918 100644 --- a/test/test_join_leave.py +++ b/test/test_join_leave.py @@ -1839,7 +1839,7 @@ def _test_theme(self): self.chrome.close_all() - def _test_create_meeting_with_password(self): + def test_create_meeting_with_password(self): wait = WebDriverWait(self.chrome, 10) roomPassword = "123123"